Compare commits
	
		
			128 Commits
		
	
	
		
			feature/ea
			...
			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 | ||
| 
						 | 
					910a6f8b34 | ||
| 
						 | 
					06253f029c | ||
| 
						 | 
					fa6527b24f | ||
| 
						 | 
					0501e6417a | ||
| 
						 | 
					a198f5252d | ||
| 
						 | 
					d83842af27 | ||
| 
						 | 
					f605f7dcc6 | ||
| 
						 | 
					e638bc04ed | ||
| 
						 | 
					4830c3ea2d | ||
| 
						 | 
					8e7c025e47 | ||
| 
						 | 
					1bfe929ab3 | ||
| 
						 | 
					93cc2c883e | ||
| 
						 | 
					44290a20a6 | ||
| 
						 | 
					1f10a284f2 | ||
| 
						 | 
					28f397574f | ||
| 
						 | 
					6c1fa6de0b | ||
| 
						 | 
					f0a08afd31 | ||
| 
						 | 
					982fc09908 | ||
| 
						 | 
					9e0b5b0b82 | ||
| 
						 | 
					b12e8dc147 | ||
| 
						 | 
					25c5a3297c | ||
| 
						 | 
					dd3ad42eb5 | ||
| 
						 | 
					5ea181829e | ||
| 
						 | 
					0cf203669f | ||
| 
						 | 
					559bfcac60 | ||
| 
						 | 
					db8a1ed0ab | ||
| 
						 | 
					16150905a0 | ||
| 
						 | 
					9a376887ac | ||
| 
						 | 
					773808fa59 | ||
| 
						 | 
					c1e59a0676 | ||
| 
						 | 
					05febc60bd | ||
| 
						 | 
					a73fe598ef | ||
| 
						 | 
					b7f20fed6c | ||
| 
						 | 
					585923c827 | ||
| 
						 | 
					394e17d599 | ||
| 
						 | 
					59136850b8 | ||
| 
						 | 
					d726f4b1e8 | ||
| 
						 | 
					705b9b1e6a | ||
| 
						 | 
					31e8ad8a3e | ||
| 
						 | 
					99827e005b | ||
| 
						 | 
					751c8a8bc6 | ||
| 
						 | 
					73305c0b28 | ||
| 
						 | 
					37216cd16b | ||
| 
						 | 
					dae68638cf | ||
| 
						 | 
					7cadc0bc28 | ||
| 
						 | 
					cce686f3a8 | ||
| 
						 | 
					4fe46fbcef | ||
| 
						 | 
					fe8b8f46aa | ||
| 
						 | 
					310f1a2283 | ||
| 
						 | 
					7079761ffe | ||
| 
						 | 
					f681c981c6 | ||
| 
						 | 
					5d97146d14 | ||
| 
						 | 
					7b56bd697d | ||
| 
						 | 
					14cd268d69 | ||
| 
						 | 
					754be1c9c9 | ||
| 
						 | 
					da2c155254 | ||
| 
						 | 
					ceb2888f82 | ||
| 
						 | 
					ce3e2bb32b | ||
| 
						 | 
					26c94c9ec6 | ||
| 
						 | 
					7b6eed9a47 | ||
| 
						 | 
					639197f4c8 | ||
| 
						 | 
					13bae8d2fa | ||
| 
						 | 
					6b2027550c | ||
| 
						 | 
					022b365bb2 | ||
| 
						 | 
					d8867fc9ea | ||
| 
						 | 
					118c58b5fa | ||
| 
						 | 
					faccc1367f | ||
| 
						 | 
					22b83b0814 | ||
| 
						 | 
					1d82e2a7d9 | ||
| 
						 | 
					823bd578f2 | ||
| 
						 | 
					3e5c36b39e | ||
| 
						 | 
					8fb0897160 | ||
| 
						 | 
					b8a72c57e1 | ||
| 
						 | 
					6a0a8e8ab4 | ||
| 
						 | 
					9188565a86 | ||
| 
						 | 
					4d7d22c337 | ||
| 
						 | 
					b58116b023 | ||
| 
						 | 
					fe9e5ce861 | ||
| 
						 | 
					e43d53e564 | ||
| 
						 | 
					d4a5039efc | ||
| 
						 | 
					35506e0175 | ||
| 
						 | 
					1c27831f92 | ||
| 
						 | 
					cdbf07a835 | ||
| 
						 | 
					b92580943a | ||
| 
						 | 
					60eff1000f | ||
| 
						 | 
					96510b270d | ||
| 
						 | 
					1281104d96 | ||
| 
						 | 
					3c1724fa81 | ||
| 
						 | 
					1630af4fbd | ||
| 
						 | 
					e76e2b1537 | ||
| 
						 | 
					6c276dc596 | ||
| 
						 | 
					d3c115e3f9 | ||
| 
						 | 
					c245ef7149 | ||
| 
						 | 
					8b09ba2924 | ||
| 
						 | 
					52eb310f95 | ||
| 
						 | 
					5bff38fc7b | ||
| 
						 | 
					2813a59323 | ||
| 
						 | 
					eef33fa263 | ||
| 
						 | 
					241d3cea53 | ||
| 
						 | 
					89d6db4208 | ||
| 
						 | 
					e0ad288cf4 | ||
| 
						 | 
					f4d7fae8ca | ||
| 
						 | 
					95a7493fc1 | ||
| 
						 | 
					8243dbcbef | ||
| 
						 | 
					c3a4071627 | ||
| 
						 | 
					cef3f22e0d | ||
| 
						 | 
					c206b965ad | 
							
								
								
									
										8
									
								
								.github/actions/compile_messages/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					name: "Compile messages"
 | 
				
			||||||
 | 
					description: "Compile the gettext translation messages"
 | 
				
			||||||
 | 
					runs:
 | 
				
			||||||
 | 
					  using: composite
 | 
				
			||||||
 | 
					  steps:
 | 
				
			||||||
 | 
					      - name: Setup project
 | 
				
			||||||
 | 
					        run: poetry run ./manage.py compilemessages
 | 
				
			||||||
 | 
					        shell: bash
 | 
				
			||||||
							
								
								
									
										53
									
								
								.github/actions/setup_project/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					name: "Setup project"
 | 
				
			||||||
 | 
					description: "Setup Python and Poetry"
 | 
				
			||||||
 | 
					runs:
 | 
				
			||||||
 | 
					  using: composite
 | 
				
			||||||
 | 
					  steps:
 | 
				
			||||||
 | 
					    - name: Install apt packages
 | 
				
			||||||
 | 
					      uses: awalsh128/cache-apt-pkgs-action@latest
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        packages: gettext libxapian-dev libgraphviz-dev
 | 
				
			||||||
 | 
					        version: 1.0  # increment to reset cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install dependencies
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        sudo apt update
 | 
				
			||||||
 | 
					        sudo apt install gettext libxapian-dev libgraphviz-dev
 | 
				
			||||||
 | 
					      shell: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Set up python
 | 
				
			||||||
 | 
					      uses: actions/setup-python@v4
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        python-version: "3.10"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Load cached Poetry installation
 | 
				
			||||||
 | 
					      id: cached-poetry
 | 
				
			||||||
 | 
					      uses: actions/cache@v3
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        path: ~/.local
 | 
				
			||||||
 | 
					        key: poetry-0  # increment to reset cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install Poetry
 | 
				
			||||||
 | 
					      if: steps.cached-poetry.outputs.cache-hit != 'true'
 | 
				
			||||||
 | 
					      shell: bash
 | 
				
			||||||
 | 
					      run: curl -sSL https://install.python-poetry.org | python3 -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Check pyproject.toml syntax
 | 
				
			||||||
 | 
					      shell: bash
 | 
				
			||||||
 | 
					      run: poetry check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Load cached dependencies
 | 
				
			||||||
 | 
					      uses: actions/cache@v3
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        path: ~/.cache/pypoetry
 | 
				
			||||||
 | 
					        key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
 | 
				
			||||||
 | 
					        restore-keys: |
 | 
				
			||||||
 | 
					          ${{ runner.os }}-poetry-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install dependencies
 | 
				
			||||||
 | 
					      run: poetry install -E testing -E docs
 | 
				
			||||||
 | 
					      shell: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Compile gettext messages
 | 
				
			||||||
 | 
					      run: poetry run ./manage.py compilemessages
 | 
				
			||||||
 | 
					      shell: bash
 | 
				
			||||||
							
								
								
									
										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
 | 
				
			||||||
							
								
								
									
										18
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					# To get started with Dependabot version updates, you'll need to specify which
 | 
				
			||||||
 | 
					# package ecosystems to update and where the package manifests are located.
 | 
				
			||||||
 | 
					# Please see the documentation for all configuration options:
 | 
				
			||||||
 | 
					# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					version: 2
 | 
				
			||||||
 | 
					updates:
 | 
				
			||||||
 | 
					  - package-ecosystem: "pip" # See documentation for possible values
 | 
				
			||||||
 | 
					    directory: "/" # Location of package manifests
 | 
				
			||||||
 | 
					    schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					    # Raise pull requests for version updates
 | 
				
			||||||
 | 
					    # to pip against the `develop` branch
 | 
				
			||||||
 | 
					    target-branch: "taiste"
 | 
				
			||||||
 | 
					    reviewers:
 | 
				
			||||||
 | 
					      - "ae-utbm/developpers-v3"
 | 
				
			||||||
 | 
					    commit-message:
 | 
				
			||||||
 | 
					      prefix: "[UPDATE] "
 | 
				
			||||||
							
								
								
									
										43
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					name: Sith 3 CI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					      - taiste
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					      - taiste
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  black:
 | 
				
			||||||
 | 
					    name: Black format
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Check out repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v3
 | 
				
			||||||
 | 
					      - name: Setup Project
 | 
				
			||||||
 | 
					        uses: ./.github/actions/setup_project
 | 
				
			||||||
 | 
					      - run: poetry run black --check .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tests:
 | 
				
			||||||
 | 
					    name: Run tests and generate coverage report
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					      - name: Generate coverage report
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          poetry run coverage report
 | 
				
			||||||
 | 
					          poetry run coverage html
 | 
				
			||||||
 | 
					      - name: Archive code coverage results
 | 
				
			||||||
 | 
					        uses: actions/upload-artifact@v3
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          name: coverage-report
 | 
				
			||||||
 | 
					          path: coverage_report
 | 
				
			||||||
							
								
								
									
										4
									
								
								.github/workflows/deploy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -33,11 +33,11 @@ jobs:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # See https://github.com/ae-utbm/sith3/wiki/GitHub-Actions#deployment-action
 | 
					        # See https://github.com/ae-utbm/sith3/wiki/GitHub-Actions#deployment-action
 | 
				
			||||||
        script: |
 | 
					        script: |
 | 
				
			||||||
          export PATH="$HOME/.poetry/bin:$PATH"
 | 
					          export PATH="/home/sith/.local/bin:$PATH"
 | 
				
			||||||
          pushd ${{secrets.SITH_PATH}}
 | 
					          pushd ${{secrets.SITH_PATH}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          git pull
 | 
					          git pull
 | 
				
			||||||
          poetry update
 | 
					          poetry install
 | 
				
			||||||
          poetry run ./manage.py migrate
 | 
					          poetry run ./manage.py migrate
 | 
				
			||||||
          echo "yes" | poetry run ./manage.py collectstatic
 | 
					          echo "yes" | poetry run ./manage.py collectstatic
 | 
				
			||||||
          poetry run ./manage.py compilestatic
 | 
					          poetry run ./manage.py compilestatic
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								.github/workflows/taiste.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					name: Sith3 taiste
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [ taiste ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  deployment:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    environment: taiste
 | 
				
			||||||
 | 
					    timeout-minutes: 30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - name: SSH Remote Commands
 | 
				
			||||||
 | 
					      uses: appleboy/ssh-action@dce9d565de8d876c11d93fa4fe677c0285a66d78
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        # Proxy
 | 
				
			||||||
 | 
					        proxy_host : ${{secrets.PROXY_HOST}}
 | 
				
			||||||
 | 
					        proxy_port : ${{secrets.PROXY_PORT}}
 | 
				
			||||||
 | 
					        proxy_username : ${{secrets.PROXY_USER}}
 | 
				
			||||||
 | 
					        proxy_passphrase: ${{secrets.PROXY_PASSPHRASE}}
 | 
				
			||||||
 | 
					        proxy_key: ${{secrets.PROXY_KEY}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Serveur web
 | 
				
			||||||
 | 
					        host: ${{secrets.HOST}}
 | 
				
			||||||
 | 
					        port : ${{secrets.PORT}}
 | 
				
			||||||
 | 
					        username : ${{secrets.USER}}
 | 
				
			||||||
 | 
					        key: ${{secrets.KEY}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        script_stop: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # See https://github.com/ae-utbm/sith3/wiki/GitHub-Actions#deployment-action
 | 
				
			||||||
 | 
					        script: |
 | 
				
			||||||
 | 
					          export PATH="$HOME/.poetry/bin:$PATH"
 | 
				
			||||||
 | 
					          pushd ${{secrets.SITH_PATH}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          git pull
 | 
				
			||||||
 | 
					          poetry install
 | 
				
			||||||
 | 
					          poetry run ./manage.py migrate
 | 
				
			||||||
 | 
					          echo "yes" | poetry run ./manage.py collectstatic
 | 
				
			||||||
 | 
					          poetry run ./manage.py compilestatic
 | 
				
			||||||
 | 
					          poetry run ./manage.py compilemessages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          sudo systemctl restart uwsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sentry:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    environment: taiste
 | 
				
			||||||
 | 
					    timeout-minutes: 30
 | 
				
			||||||
 | 
					    needs: deployment
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Sentry Release
 | 
				
			||||||
 | 
					        uses: getsentry/action-release@v1.2.0
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
 | 
				
			||||||
 | 
					          SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
 | 
				
			||||||
 | 
					          SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
 | 
				
			||||||
 | 
					          SENTRY_URL: ${{ secrets.SENTRY_URL }}
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          environment: taiste
 | 
				
			||||||
							
								
								
									
										83
									
								
								.github/workflows/unittests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,83 +0,0 @@
 | 
				
			|||||||
name: Sith3 CI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
on:
 | 
					 | 
				
			||||||
  pull_request:
 | 
					 | 
				
			||||||
    branches: [ master ]
 | 
					 | 
				
			||||||
  push:
 | 
					 | 
				
			||||||
    branches: [ master ]
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
jobs:
 | 
					 | 
				
			||||||
  unittests:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    timeout-minutes: 30
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      # Skip unit testing if no diff on .py files
 | 
					 | 
				
			||||||
      - name: Check file diff
 | 
					 | 
				
			||||||
        uses: technote-space/get-diff-action@v6
 | 
					 | 
				
			||||||
        id: git-diff
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          PATTERNS: |
 | 
					 | 
				
			||||||
            **/*.py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Set up python
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        uses: actions/setup-python@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          python-version: '3.8'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Install dependencies
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          sudo apt-get update
 | 
					 | 
				
			||||||
          sudo apt-get install gettext libxapian-dev libgraphviz-dev
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Install poetry
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          python -m pip install --upgrade pip
 | 
					 | 
				
			||||||
          python -m pip install poetry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Checking pyproject.toml syntax
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: poetry check
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Install project
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: poetry install -E testing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Setup xapian index
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          mkdir -p /dev/shm/search_indexes
 | 
					 | 
				
			||||||
          ln -s /dev/shm/search_indexes sith/search_indexes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Setup project
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: poetry run ./manage.py compilemessages
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
      - name: Launch tests and generate coverage report
 | 
					 | 
				
			||||||
        if: steps.git-diff.outputs.diff
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          poetry run coverage run ./manage.py test
 | 
					 | 
				
			||||||
          poetry run coverage report
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  lint:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					 | 
				
			||||||
      - name: Set up python
 | 
					 | 
				
			||||||
        uses: actions/setup-python@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          python-version: '3.8'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Install black
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          python -m pip install --upgrade pip
 | 
					 | 
				
			||||||
          python -m pip install black==22.6.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Check linting
 | 
					 | 
				
			||||||
        run: black --check .
 | 
					 | 
				
			||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -7,9 +7,11 @@ db.sqlite3
 | 
				
			|||||||
pyrightconfig.json
 | 
					pyrightconfig.json
 | 
				
			||||||
dist/
 | 
					dist/
 | 
				
			||||||
.vscode/
 | 
					.vscode/
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
env/
 | 
					env/
 | 
				
			||||||
doc/html
 | 
					doc/html
 | 
				
			||||||
data/
 | 
					data/
 | 
				
			||||||
 | 
					galaxy/test_galaxy_state.json
 | 
				
			||||||
/static/
 | 
					/static/
 | 
				
			||||||
sith/settings_custom.py
 | 
					sith/settings_custom.py
 | 
				
			||||||
sith/search_indexes/
 | 
					sith/search_indexes/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						@@ -16,3 +16,4 @@ Zar <antoine.charmeau@utbm.fr> <antoine.charmeau@laposte.net>
 | 
				
			|||||||
root <root@localhost.localdomain>
 | 
					root <root@localhost.localdomain>
 | 
				
			||||||
tleb <tleb@openmailbox.org> <theo.lebrun@live.fr>
 | 
					tleb <tleb@openmailbox.org> <theo.lebrun@live.fr>
 | 
				
			||||||
tleb <tleb@openmailbox.org> <theo.lebrun@utbm.fr>
 | 
					tleb <tleb@openmailbox.org> <theo.lebrun@utbm.fr>
 | 
				
			||||||
 | 
					Maréchal <thgirod@hotmail.com>
 | 
				
			||||||
@@ -18,7 +18,7 @@ formats: all
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Optionally set the version of Python and requirements required to build your docs
 | 
					# Optionally set the version of Python and requirements required to build your docs
 | 
				
			||||||
python:
 | 
					python:
 | 
				
			||||||
  version: 3.8
 | 
					  version: "3.8"
 | 
				
			||||||
  install:
 | 
					  install:
 | 
				
			||||||
    - method: pip
 | 
					    - method: pip
 | 
				
			||||||
      path: .
 | 
					      path: .
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								LICENSE.old
									
									
									
									
									
								
							
							
						
						@@ -1,21 +0,0 @@
 | 
				
			|||||||
The MIT License (MIT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (c) 2016 Skia
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
					 | 
				
			||||||
in the Software without restriction, including without limitation the rights
 | 
					 | 
				
			||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The above copyright notice and this permission notice shall be included in all
 | 
					 | 
				
			||||||
copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
					 | 
				
			||||||
SOFTWARE.
 | 
					 | 
				
			||||||
@@ -9,7 +9,7 @@
 | 
				
			|||||||
    <img src="https://img.shields.io/readthedocs/sith-ae?logo=readthedocs&style=for-the-badge">
 | 
					    <img src="https://img.shields.io/readthedocs/sith-ae?logo=readthedocs&style=for-the-badge">
 | 
				
			||||||
  </a>
 | 
					  </a>
 | 
				
			||||||
  <a href="https://discord.gg/XK9WfPsUFm">
 | 
					  <a href="https://discord.gg/XK9WfPsUFm">
 | 
				
			||||||
    <img src="https://img.shields.io/discord/889796155523874847?label=Discord&logo=discord&style=for-the-badge">
 | 
					    <img src="https://img.shields.io/discord/971448179075731476?label=Discord&logo=discord&style=for-the-badge">
 | 
				
			||||||
  </a>
 | 
					  </a>
 | 
				
			||||||
</p>
 | 
					</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,5 +37,4 @@
 | 
				
			|||||||
  </li>
 | 
					  </li>
 | 
				
			||||||
</ul>
 | 
					</ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> This project is licenced under GNU GPL, see the LICENSE file at the top of the repository for more details.
 | 
					> This project is licensed under GNU GPL, see the LICENSE file at the top of the repository for more details.
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = []
 | 
					    dependencies = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        ("club", "0001_initial"),
 | 
					        ("club", "0001_initial"),
 | 
				
			||||||
        ("accounting", "0001_initial"),
 | 
					        ("accounting", "0001_initial"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import phonenumber_field.modelfields
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("accounting", "0002_auto_20160824_2152")]
 | 
					    dependencies = [("accounting", "0002_auto_20160824_2152")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("accounting", "0003_auto_20160824_2203")]
 | 
					    dependencies = [("accounting", "0003_auto_20160824_2203")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("accounting", "0004_auto_20161005_1505")]
 | 
					    dependencies = [("accounting", "0004_auto_20161005_1505")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,7 +66,7 @@ class Company(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -125,7 +117,9 @@ class BankAccount(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        m = self.club.get_membership_for(user)
 | 
					        m = self.club.get_membership_for(user)
 | 
				
			||||||
        if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
 | 
					        if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
 | 
				
			||||||
@@ -162,7 +156,9 @@ class ClubAccount(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -233,7 +229,9 @@ class GeneralJournal(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        if self.club_account.can_be_edited_by(user):
 | 
					        if self.club_account.can_be_edited_by(user):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
@@ -243,7 +241,7 @@ class GeneralJournal(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        if self.club_account.can_be_edited_by(user):
 | 
					        if self.club_account.can_be_edited_by(user):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
@@ -422,7 +420,9 @@ class Operation(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        if self.journal.closed:
 | 
					        if self.journal.closed:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
@@ -435,7 +435,7 @@ class Operation(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        if self.journal.closed:
 | 
					        if self.journal.closed:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
@@ -491,7 +491,9 @@ class AccountingType(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Method to see if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -562,6 +564,8 @@ class Label(models.Model):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
        return self.club_account.is_owned_by(user)
 | 
					        return self.club_account.is_owned_by(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_edited_by(self, user):
 | 
					    def can_be_edited_by(self, user):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@
 | 
				
			|||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <hr>
 | 
					        <hr>
 | 
				
			||||||
        <h2>{% trans %}Bank account: {% endtrans %}{{ object.name }}</h2>
 | 
					        <h2>{% trans %}Bank account: {% endtrans %}{{ object.name }}</h2>
 | 
				
			||||||
        {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %}
 | 
					        {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %}
 | 
				
			||||||
        <a href="{{ url('accounting:bank_delete', b_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
					        <a href="{{ url('accounting:bank_delete', b_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        <h4>{% trans %}Infos{% endtrans %}</h4>
 | 
					        <h4>{% trans %}Infos{% endtrans %}</h4>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@
 | 
				
			|||||||
        <h4>
 | 
					        <h4>
 | 
				
			||||||
        {% trans %}Accounting{% endtrans %}
 | 
					        {% trans %}Accounting{% endtrans %}
 | 
				
			||||||
        </h4>
 | 
					        </h4>
 | 
				
			||||||
        {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
					        {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
				
			||||||
        <p><a href="{{ url('accounting:simple_type_list') }}">{% trans %}Manage simplified types{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:simple_type_list') }}">{% trans %}Manage simplified types{% endtrans %}</a></p>
 | 
				
			||||||
        <p><a href="{{ url('accounting:type_list') }}">{% trans %}Manage accounting types{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:type_list') }}">{% trans %}Manage accounting types{% endtrans %}</a></p>
 | 
				
			||||||
        <p><a href="{{ url('accounting:bank_new') }}">{% trans %}New bank account{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:bank_new') }}">{% trans %}New bank account{% endtrans %}</a></p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@
 | 
				
			|||||||
        {% if user.is_root and not object.journals.exists() %}
 | 
					        {% if user.is_root and not object.journals.exists() %}
 | 
				
			||||||
        <a href="{{ url('accounting:club_delete', c_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
					        <a href="{{ url('accounting:club_delete', c_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
					        {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
				
			||||||
        <p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        <p><a href="{{ url('accounting:label_list', clubaccount_id=object.id) }}">{% trans %}Label list{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:label_list', clubaccount_id=object.id) }}">{% trans %}Label list{% endtrans %}</a></p>
 | 
				
			||||||
@@ -56,7 +56,7 @@
 | 
				
			|||||||
                {% endif %}
 | 
					                {% endif %}
 | 
				
			||||||
                <td> <a href="{{ url('accounting:journal_details', j_id=j.id) }}">{% trans %}View{% endtrans %}</a>
 | 
					                <td> <a href="{{ url('accounting:journal_details', j_id=j.id) }}">{% trans %}View{% endtrans %}</a>
 | 
				
			||||||
                    <a href="{{ url('accounting:journal_edit', j_id=j.id) }}">{% trans %}Edit{% endtrans %}</a>
 | 
					                    <a href="{{ url('accounting:journal_edit', j_id=j.id) }}">{% trans %}Edit{% endtrans %}</a>
 | 
				
			||||||
                    {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %}
 | 
					                    {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %}
 | 
				
			||||||
                        <a href="{{ url('accounting:journal_delete', j_id=j.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
					                        <a href="{{ url('accounting:journal_delete', j_id=j.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
				
			||||||
                    {% endif %}
 | 
					                    {% endif %}
 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,11 +6,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
    <div id="accounting">
 | 
					    <div id="accounting">
 | 
				
			||||||
        {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) or user.is_root %}
 | 
					        {% if user.is_root
 | 
				
			||||||
 | 
					           or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
 | 
				
			||||||
 | 
					        %}
 | 
				
			||||||
        <p><a href="{{ url('accounting:co_new') }}">{% trans %}Create new company{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:co_new') }}">{% trans %}Create new company{% endtrans %}</a></p>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					        <br/>
 | 
				
			||||||
        </br>
 | 
					 | 
				
			||||||
        <table>
 | 
					        <table>
 | 
				
			||||||
            <thead>
 | 
					            <thead>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,10 +84,13 @@
 | 
				
			|||||||
                <td>-</td>
 | 
					                <td>-</td>
 | 
				
			||||||
                {% endif %}
 | 
					                {% endif %}
 | 
				
			||||||
                <td>
 | 
					                <td>
 | 
				
			||||||
                    {% if o.journal.club_account.bank_account.name != "AE TI" and o.journal.club_account.bank_account.name != "TI" or user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
					                    {%
 | 
				
			||||||
                    {% if not o.journal.closed %}
 | 
					                        if o.journal.club_account.bank_account.name not in ["AE TI", "TI"]
 | 
				
			||||||
                    <a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
 | 
					                        or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
 | 
				
			||||||
                    {% endif %}
 | 
					                    %}
 | 
				
			||||||
 | 
					                        {% if not o.journal.closed %}
 | 
				
			||||||
 | 
					                            <a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
 | 
				
			||||||
 | 
					                        {% endif %}
 | 
				
			||||||
                    {% endif %}
 | 
					                    {% endif %}
 | 
				
			||||||
                </td>
 | 
					                </td>
 | 
				
			||||||
                <td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
 | 
					                <td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <hr>
 | 
					        <hr>
 | 
				
			||||||
        <p><a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{% trans %}Back to club account{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{% trans %}Back to club account{% endtrans %}</a></p>
 | 
				
			||||||
        {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
					        {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
				
			||||||
        <p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
 | 
					        <p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        {% if object.labels.all() %}
 | 
					        {% if object.labels.all() %}
 | 
				
			||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
        <ul>
 | 
					        <ul>
 | 
				
			||||||
            {% for l in object.labels.all()  %}
 | 
					            {% for l in object.labels.all()  %}
 | 
				
			||||||
            <li><a href="{{ url('accounting:label_edit', label_id=l.id) }}">{{ l }}</a>
 | 
					            <li><a href="{{ url('accounting:label_edit', label_id=l.id) }}">{{ l }}</a>
 | 
				
			||||||
            {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
					            {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
 | 
				
			||||||
             -
 | 
					             -
 | 
				
			||||||
                <a href="{{ url('accounting:label_delete', label_id=l.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
					                <a href="{{ url('accounting:label_delete', label_id=l.id) }}">{% trans %}Delete{% endtrans %}</a>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,7 +31,6 @@ from accounting.models import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class RefoundAccountTest(TestCase):
 | 
					class RefoundAccountTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
        self.skia = User.objects.filter(username="skia").first()
 | 
					        self.skia = User.objects.filter(username="skia").first()
 | 
				
			||||||
        # reffil skia's account
 | 
					        # reffil skia's account
 | 
				
			||||||
        self.skia.customer.amount = 800
 | 
					        self.skia.customer.amount = 800
 | 
				
			||||||
@@ -81,7 +72,6 @@ class RefoundAccountTest(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class JournalTest(TestCase):
 | 
					class JournalTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
        self.journal = GeneralJournal.objects.filter(id=1).first()
 | 
					        self.journal = GeneralJournal.objects.filter(id=1).first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_permission_granted(self):
 | 
					    def test_permission_granted(self):
 | 
				
			||||||
@@ -109,7 +99,6 @@ class JournalTest(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class OperationTest(TestCase):
 | 
					class OperationTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
        self.tomorrow_formatted = (date.today() + timedelta(days=1)).strftime(
 | 
					        self.tomorrow_formatted = (date.today() + timedelta(days=1)).strftime(
 | 
				
			||||||
            "%d/%m/%Y"
 | 
					            "%d/%m/%Y"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,154 +1,140 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.urls import re_path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from accounting.views import *
 | 
					from accounting.views import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    # Accounting types
 | 
					    # Accounting types
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^simple_type$",
 | 
					        "simple_type/",
 | 
				
			||||||
        SimplifiedAccountingTypeListView.as_view(),
 | 
					        SimplifiedAccountingTypeListView.as_view(),
 | 
				
			||||||
        name="simple_type_list",
 | 
					        name="simple_type_list",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^simple_type/create$",
 | 
					        "simple_type/create/",
 | 
				
			||||||
        SimplifiedAccountingTypeCreateView.as_view(),
 | 
					        SimplifiedAccountingTypeCreateView.as_view(),
 | 
				
			||||||
        name="simple_type_new",
 | 
					        name="simple_type_new",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^simple_type/(?P<type_id>[0-9]+)/edit$",
 | 
					        "simple_type/<int:type_id>/edit/",
 | 
				
			||||||
        SimplifiedAccountingTypeEditView.as_view(),
 | 
					        SimplifiedAccountingTypeEditView.as_view(),
 | 
				
			||||||
        name="simple_type_edit",
 | 
					        name="simple_type_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Accounting types
 | 
					    # Accounting types
 | 
				
			||||||
    re_path(r"^type$", AccountingTypeListView.as_view(), name="type_list"),
 | 
					    path("type/", AccountingTypeListView.as_view(), name="type_list"),
 | 
				
			||||||
    re_path(r"^type/create$", AccountingTypeCreateView.as_view(), name="type_new"),
 | 
					    path("type/create/", AccountingTypeCreateView.as_view(), name="type_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^type/(?P<type_id>[0-9]+)/edit$",
 | 
					        "type/<int:type_id>/edit/",
 | 
				
			||||||
        AccountingTypeEditView.as_view(),
 | 
					        AccountingTypeEditView.as_view(),
 | 
				
			||||||
        name="type_edit",
 | 
					        name="type_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Bank accounts
 | 
					    # Bank accounts
 | 
				
			||||||
    re_path(r"^$", BankAccountListView.as_view(), name="bank_list"),
 | 
					    path("", BankAccountListView.as_view(), name="bank_list"),
 | 
				
			||||||
    re_path(r"^bank/create$", BankAccountCreateView.as_view(), name="bank_new"),
 | 
					    path("bank/create", BankAccountCreateView.as_view(), name="bank_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^bank/(?P<b_account_id>[0-9]+)$",
 | 
					        "bank/<int:b_account_id>/",
 | 
				
			||||||
        BankAccountDetailView.as_view(),
 | 
					        BankAccountDetailView.as_view(),
 | 
				
			||||||
        name="bank_details",
 | 
					        name="bank_details",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^bank/(?P<b_account_id>[0-9]+)/edit$",
 | 
					        "bank/<int:b_account_id>/edit/",
 | 
				
			||||||
        BankAccountEditView.as_view(),
 | 
					        BankAccountEditView.as_view(),
 | 
				
			||||||
        name="bank_edit",
 | 
					        name="bank_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^bank/(?P<b_account_id>[0-9]+)/delete$",
 | 
					        "bank/<int:b_account_id>/delete/",
 | 
				
			||||||
        BankAccountDeleteView.as_view(),
 | 
					        BankAccountDeleteView.as_view(),
 | 
				
			||||||
        name="bank_delete",
 | 
					        name="bank_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Club accounts
 | 
					    # Club accounts
 | 
				
			||||||
    re_path(r"^club/create$", ClubAccountCreateView.as_view(), name="club_new"),
 | 
					    path("club/create/", ClubAccountCreateView.as_view(), name="club_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^club/(?P<c_account_id>[0-9]+)$",
 | 
					        "club/<int:c_account_id>/",
 | 
				
			||||||
        ClubAccountDetailView.as_view(),
 | 
					        ClubAccountDetailView.as_view(),
 | 
				
			||||||
        name="club_details",
 | 
					        name="club_details",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^club/(?P<c_account_id>[0-9]+)/edit$",
 | 
					        "club/<int:c_account_id>/edit/",
 | 
				
			||||||
        ClubAccountEditView.as_view(),
 | 
					        ClubAccountEditView.as_view(),
 | 
				
			||||||
        name="club_edit",
 | 
					        name="club_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^club/(?P<c_account_id>[0-9]+)/delete$",
 | 
					        "club/<int:c_account_id>/delete/",
 | 
				
			||||||
        ClubAccountDeleteView.as_view(),
 | 
					        ClubAccountDeleteView.as_view(),
 | 
				
			||||||
        name="club_delete",
 | 
					        name="club_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Journals
 | 
					    # Journals
 | 
				
			||||||
    re_path(r"^journal/create$", JournalCreateView.as_view(), name="journal_new"),
 | 
					    path("journal/create/", JournalCreateView.as_view(), name="journal_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)$",
 | 
					        "journal/<int:j_id>/",
 | 
				
			||||||
        JournalDetailView.as_view(),
 | 
					        JournalDetailView.as_view(),
 | 
				
			||||||
        name="journal_details",
 | 
					        name="journal_details",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)/edit$",
 | 
					        "journal/<int:j_id>/edit/",
 | 
				
			||||||
        JournalEditView.as_view(),
 | 
					        JournalEditView.as_view(),
 | 
				
			||||||
        name="journal_edit",
 | 
					        name="journal_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)/delete$",
 | 
					        "journal/<int:j_id>/delete/",
 | 
				
			||||||
        JournalDeleteView.as_view(),
 | 
					        JournalDeleteView.as_view(),
 | 
				
			||||||
        name="journal_delete",
 | 
					        name="journal_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)/statement/nature$",
 | 
					        "journal/<int:j_id>/statement/nature/",
 | 
				
			||||||
        JournalNatureStatementView.as_view(),
 | 
					        JournalNatureStatementView.as_view(),
 | 
				
			||||||
        name="journal_nature_statement",
 | 
					        name="journal_nature_statement",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)/statement/person$",
 | 
					        "journal/<int:j_id>/statement/person/",
 | 
				
			||||||
        JournalPersonStatementView.as_view(),
 | 
					        JournalPersonStatementView.as_view(),
 | 
				
			||||||
        name="journal_person_statement",
 | 
					        name="journal_person_statement",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^journal/(?P<j_id>[0-9]+)/statement/accounting$",
 | 
					        "journal/<int:j_id>/statement/accounting/",
 | 
				
			||||||
        JournalAccountingStatementView.as_view(),
 | 
					        JournalAccountingStatementView.as_view(),
 | 
				
			||||||
        name="journal_accounting_statement",
 | 
					        name="journal_accounting_statement",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # Operations
 | 
					    # Operations
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^operation/create/(?P<j_id>[0-9]+)$",
 | 
					        "operation/create/<int:j_id>/",
 | 
				
			||||||
        OperationCreateView.as_view(),
 | 
					        OperationCreateView.as_view(),
 | 
				
			||||||
        name="op_new",
 | 
					        name="op_new",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("operation/<int:op_id>/", OperationEditView.as_view(), name="op_edit"),
 | 
				
			||||||
        r"^operation/(?P<op_id>[0-9]+)$", OperationEditView.as_view(), name="op_edit"
 | 
					    path("operation/<int:op_id>/pdf/", OperationPDFView.as_view(), name="op_pdf"),
 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^operation/(?P<op_id>[0-9]+)/pdf$", OperationPDFView.as_view(), name="op_pdf"
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    # Companies
 | 
					    # Companies
 | 
				
			||||||
    re_path(r"^company/list$", CompanyListView.as_view(), name="co_list"),
 | 
					    path("company/list/", CompanyListView.as_view(), name="co_list"),
 | 
				
			||||||
    re_path(r"^company/create$", CompanyCreateView.as_view(), name="co_new"),
 | 
					    path("company/create/", CompanyCreateView.as_view(), name="co_new"),
 | 
				
			||||||
    re_path(r"^company/(?P<co_id>[0-9]+)$", CompanyEditView.as_view(), name="co_edit"),
 | 
					    path("company/<int:co_id>/", CompanyEditView.as_view(), name="co_edit"),
 | 
				
			||||||
    # Labels
 | 
					    # Labels
 | 
				
			||||||
    re_path(r"^label/new$", LabelCreateView.as_view(), name="label_new"),
 | 
					    path("label/new/", LabelCreateView.as_view(), name="label_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^label/(?P<clubaccount_id>[0-9]+)$",
 | 
					        "label/<int:clubaccount_id>/",
 | 
				
			||||||
        LabelListView.as_view(),
 | 
					        LabelListView.as_view(),
 | 
				
			||||||
        name="label_list",
 | 
					        name="label_list",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("label/<int:label_id>/edit/", LabelEditView.as_view(), name="label_edit"),
 | 
				
			||||||
        r"^label/(?P<label_id>[0-9]+)/edit$", LabelEditView.as_view(), name="label_edit"
 | 
					    path(
 | 
				
			||||||
    ),
 | 
					        "label/<int:label_id>/delete/",
 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^label/(?P<label_id>[0-9]+)/delete$",
 | 
					 | 
				
			||||||
        LabelDeleteView.as_view(),
 | 
					        LabelDeleteView.as_view(),
 | 
				
			||||||
        name="label_delete",
 | 
					        name="label_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    # User account
 | 
					    # User account
 | 
				
			||||||
    re_path(r"^refound/account$", RefoundAccountView.as_view(), name="refound_account"),
 | 
					    path("refound/account/", RefoundAccountView.as_view(), name="refound_account"),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -899,7 +891,7 @@ class RefoundAccountView(FormView):
 | 
				
			|||||||
    form_class = CloseCustomerAccountForm
 | 
					    form_class = CloseCustomerAccountForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def permission(self, user):
 | 
					    def permission(self, user):
 | 
				
			||||||
        if user.is_root or user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
					        if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise PermissionDenied
 | 
					            raise PermissionDenied
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								api/admin.py
									
									
									
									
									
								
							
							
						
						@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								api/tests.py
									
									
									
									
									
								
							
							
						
						@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								api/urls.py
									
									
									
									
									
								
							
							
						
						@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +24,6 @@ from api.views import RightModelViewSet
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CounterSerializer(serializers.ModelSerializer):
 | 
					class CounterSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    is_open = serializers.BooleanField(read_only=True)
 | 
					    is_open = serializers.BooleanField(read_only=True)
 | 
				
			||||||
    barman_list = serializers.ListField(
 | 
					    barman_list = serializers.ListField(
 | 
				
			||||||
        child=serializers.IntegerField(), read_only=True
 | 
					        child=serializers.IntegerField(), read_only=True
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +24,6 @@ from api.views import RightModelViewSet
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LaunderettePlaceSerializer(serializers.ModelSerializer):
 | 
					class LaunderettePlaceSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    machine_list = serializers.ListField(
 | 
					    machine_list = serializers.ListField(
 | 
				
			||||||
        child=serializers.IntegerField(), read_only=True
 | 
					        child=serializers.IntegerField(), read_only=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,36 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					from ajax_select import make_ajax_form
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from club.models import Club, Membership
 | 
					from club.models import Club, Membership
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
admin.site.register(Club)
 | 
					@admin.register(Club)
 | 
				
			||||||
admin.site.register(Membership)
 | 
					class ClubAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = ("name", "unix_name", "parent", "is_active")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Membership)
 | 
				
			||||||
 | 
					class MembershipAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = ("user", "club", "role", "start_date", "end_date")
 | 
				
			||||||
 | 
					    search_fields = (
 | 
				
			||||||
 | 
					        "user__username",
 | 
				
			||||||
 | 
					        "user__first_name",
 | 
				
			||||||
 | 
					        "user__last_name",
 | 
				
			||||||
 | 
					        "club__name",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    form = make_ajax_form(Membership, {"user": "users"})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -167,7 +167,6 @@ class SellingsForm(forms.Form):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, club, *args, **kwargs):
 | 
					    def __init__(self, club, *args, **kwargs):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        super(SellingsForm, self).__init__(*args, **kwargs)
 | 
					        super(SellingsForm, self).__init__(*args, **kwargs)
 | 
				
			||||||
        self.fields["products"] = forms.ModelMultipleChoiceField(
 | 
					        self.fields["products"] = forms.ModelMultipleChoiceField(
 | 
				
			||||||
            club.products.order_by("name").filter(archived=False).all(),
 | 
					            club.products.order_by("name").filter(archived=False).all(),
 | 
				
			||||||
@@ -230,9 +229,7 @@ class ClubMemberForm(forms.Form):
 | 
				
			|||||||
                id__in=[
 | 
					                id__in=[
 | 
				
			||||||
                    ms.user.id
 | 
					                    ms.user.id
 | 
				
			||||||
                    for ms in self.club_members
 | 
					                    for ms in self.club_members
 | 
				
			||||||
                    if ms.can_be_edited_by(
 | 
					                    if ms.can_be_edited_by(self.request_user)
 | 
				
			||||||
                        self.request_user, self.request_user_membership
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
            ).all(),
 | 
					            ).all(),
 | 
				
			||||||
            label=_("Mark as old"),
 | 
					            label=_("Mark as old"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = []
 | 
					    dependencies = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
        ("club", "0001_initial"),
 | 
					        ("club", "0001_initial"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0002_auto_20160824_2152")]
 | 
					    dependencies = [("club", "0002_auto_20160824_2152")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0003_auto_20160902_2042")]
 | 
					    dependencies = [("club", "0003_auto_20160902_2042")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0004_auto_20160915_1057")]
 | 
					    dependencies = [("club", "0004_auto_20160915_1057")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ import django.utils.timezone
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0005_auto_20161120_1149")]
 | 
					    dependencies = [("club", "0005_auto_20161120_1149")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0006_auto_20161229_0040")]
 | 
					    dependencies = [("club", "0006_auto_20161229_0040")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0007_auto_20170324_0917")]
 | 
					    dependencies = [("club", "0007_auto_20170324_0917")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
        ("club", "0008_auto_20170515_2214"),
 | 
					        ("club", "0008_auto_20170515_2214"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@ def generate_club_pages(apps, schema_editor):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("core", "0024_auto_20170906_1317"), ("club", "0010_club_logo")]
 | 
					    dependencies = [("core", "0024_auto_20170906_1317"), ("club", "0010_club_logo")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0009_auto_20170822_2232")]
 | 
					    dependencies = [("club", "0009_auto_20170822_2232")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("club", "0010_auto_20170912_2028")]
 | 
					    dependencies = [("club", "0010_auto_20170912_2028")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										216
									
								
								club/models.py
									
									
									
									
									
								
							
							
						
						@@ -22,10 +22,14 @@
 | 
				
			|||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.core import validators
 | 
					from django.core import validators
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db.models import Q
 | 
				
			||||||
 | 
					from django.utils.timezone import now
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from django.core.exceptions import ValidationError, ObjectDoesNotExist
 | 
					from django.core.exceptions import ValidationError, ObjectDoesNotExist
 | 
				
			||||||
from django.db import transaction
 | 
					from django.db import transaction
 | 
				
			||||||
@@ -72,6 +76,7 @@ class Club(models.Model):
 | 
				
			|||||||
        _("short description"), max_length=1000, default="", blank=True, null=True
 | 
					        _("short description"), max_length=1000, default="", blank=True, null=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    address = models.CharField(_("address"), max_length=254)
 | 
					    address = models.CharField(_("address"), max_length=254)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # This function prevents generating migration upon settings change
 | 
					    # This function prevents generating migration upon settings change
 | 
				
			||||||
    def get_default_owner_group():
 | 
					    def get_default_owner_group():
 | 
				
			||||||
        return settings.SITH_GROUP_ROOT_ID
 | 
					        return settings.SITH_GROUP_ROOT_ID
 | 
				
			||||||
@@ -122,12 +127,22 @@ class Club(models.Model):
 | 
				
			|||||||
    def clean(self):
 | 
					    def clean(self):
 | 
				
			||||||
        self.check_loop()
 | 
					        self.check_loop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _change_unixname(self, new_name):
 | 
					    def _change_unixname(self, old_name, new_name):
 | 
				
			||||||
        c = Club.objects.filter(unix_name=new_name).first()
 | 
					        c = Club.objects.filter(unix_name=new_name).first()
 | 
				
			||||||
        if c is None:
 | 
					        if c is None:
 | 
				
			||||||
 | 
					            # Update all the groups names
 | 
				
			||||||
 | 
					            Group.objects.filter(name=old_name).update(name=new_name)
 | 
				
			||||||
 | 
					            Group.objects.filter(name=old_name + settings.SITH_BOARD_SUFFIX).update(
 | 
				
			||||||
 | 
					                name=new_name + settings.SITH_BOARD_SUFFIX
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            Group.objects.filter(name=old_name + settings.SITH_MEMBER_SUFFIX).update(
 | 
				
			||||||
 | 
					                name=new_name + settings.SITH_MEMBER_SUFFIX
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if self.home:
 | 
					            if self.home:
 | 
				
			||||||
                self.home.name = new_name
 | 
					                self.home.name = new_name
 | 
				
			||||||
                self.home.save()
 | 
					                self.home.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise ValidationError(_("A club with that unix_name already exists"))
 | 
					            raise ValidationError(_("A club with that unix_name already exists"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -171,29 +186,34 @@ class Club(models.Model):
 | 
				
			|||||||
            self.page.parent = self.parent.page
 | 
					            self.page.parent = self.parent.page
 | 
				
			||||||
            self.page.save(force_lock=True)
 | 
					            self.page.save(force_lock=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @transaction.atomic()
 | 
				
			||||||
    def save(self, *args, **kwargs):
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
        with transaction.atomic():
 | 
					        old = Club.objects.filter(id=self.id).first()
 | 
				
			||||||
            creation = False
 | 
					        creation = old is None
 | 
				
			||||||
            old = Club.objects.filter(id=self.id).first()
 | 
					        if not creation and old.unix_name != self.unix_name:
 | 
				
			||||||
            if not old:
 | 
					            self._change_unixname(self.unix_name)
 | 
				
			||||||
                creation = True
 | 
					        super(Club, self).save(*args, **kwargs)
 | 
				
			||||||
            else:
 | 
					        if creation:
 | 
				
			||||||
                if old.unix_name != self.unix_name:
 | 
					            board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX)
 | 
				
			||||||
                    self._change_unixname(self.unix_name)
 | 
					            board.save()
 | 
				
			||||||
            super(Club, self).save(*args, **kwargs)
 | 
					            member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
 | 
				
			||||||
            if creation:
 | 
					            member.save()
 | 
				
			||||||
                board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX)
 | 
					            subscribers = Group.objects.filter(
 | 
				
			||||||
                board.save()
 | 
					                name=settings.SITH_MAIN_MEMBERS_GROUP
 | 
				
			||||||
                member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
 | 
					            ).first()
 | 
				
			||||||
                member.save()
 | 
					            self.make_home()
 | 
				
			||||||
                subscribers = Group.objects.filter(
 | 
					            self.home.edit_groups.set([board])
 | 
				
			||||||
                    name=settings.SITH_MAIN_MEMBERS_GROUP
 | 
					            self.home.view_groups.set([member, subscribers])
 | 
				
			||||||
                ).first()
 | 
					            self.home.save()
 | 
				
			||||||
                self.make_home()
 | 
					        self.make_page()
 | 
				
			||||||
                self.home.edit_groups.set([board])
 | 
					        cache.set(f"sith_club_{self.unix_name}", self)
 | 
				
			||||||
                self.home.view_groups.set([member, subscribers])
 | 
					
 | 
				
			||||||
                self.home.save()
 | 
					    def delete(self, *args, **kwargs):
 | 
				
			||||||
            self.make_page()
 | 
					        super().delete(*args, **kwargs)
 | 
				
			||||||
 | 
					        # Invalidate the cache of this club and of its memberships
 | 
				
			||||||
 | 
					        for membership in self.members.ongoing().select_related("user"):
 | 
				
			||||||
 | 
					            cache.delete(f"membership_{self.id}_{membership.user.id}")
 | 
				
			||||||
 | 
					        cache.delete(f"sith_club_{self.unix_name}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.name
 | 
					        return self.name
 | 
				
			||||||
@@ -208,7 +228,9 @@ class Club(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be super edited by the given user
 | 
					        Method to see if that object can be super edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_board_member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_full_logo_url(self):
 | 
					    def get_full_logo_url(self):
 | 
				
			||||||
        return "https://%s%s" % (settings.SITH_URL, self.logo.url)
 | 
					        return "https://%s%s" % (settings.SITH_URL, self.logo.url)
 | 
				
			||||||
@@ -228,28 +250,89 @@ class Club(models.Model):
 | 
				
			|||||||
            return False
 | 
					            return False
 | 
				
			||||||
        return sub.was_subscribed
 | 
					        return sub.was_subscribed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _memberships = {}
 | 
					    def get_membership_for(self, user: User) -> Optional["Membership"]:
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_membership_for(self, user):
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Returns the current membership the given user
 | 
					        Return the current membership the given user.
 | 
				
			||||||
 | 
					        The result is cached.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        try:
 | 
					        if user.is_anonymous:
 | 
				
			||||||
            return Club._memberships[self.id][user.id]
 | 
					            return None
 | 
				
			||||||
        except:
 | 
					        membership = cache.get(f"membership_{self.id}_{user.id}")
 | 
				
			||||||
            m = self.members.filter(user=user.id).filter(end_date=None).first()
 | 
					        if membership == "not_member":
 | 
				
			||||||
            try:
 | 
					            return None
 | 
				
			||||||
                Club._memberships[self.id][user.id] = m
 | 
					        if membership is None:
 | 
				
			||||||
            except:
 | 
					            membership = self.members.filter(user=user, end_date=None).first()
 | 
				
			||||||
                Club._memberships[self.id] = {}
 | 
					            if membership is None:
 | 
				
			||||||
                Club._memberships[self.id][user.id] = m
 | 
					                cache.set(f"membership_{self.id}_{user.id}", "not_member")
 | 
				
			||||||
            return m
 | 
					            else:
 | 
				
			||||||
 | 
					                cache.set(f"membership_{self.id}_{user.id}", membership)
 | 
				
			||||||
 | 
					        return membership
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def has_rights_in_club(self, user):
 | 
					    def has_rights_in_club(self, user):
 | 
				
			||||||
        m = self.get_membership_for(user)
 | 
					        m = self.get_membership_for(user)
 | 
				
			||||||
        return m is not None and m.role > settings.SITH_MAXIMUM_FREE_ROLE
 | 
					        return m is not None and m.role > settings.SITH_MAXIMUM_FREE_ROLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MembershipQuerySet(models.QuerySet):
 | 
				
			||||||
 | 
					    def ongoing(self) -> "MembershipQuerySet":
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Filter all memberships which are not finished yet
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # noinspection PyTypeChecker
 | 
				
			||||||
 | 
					        return self.filter(Q(end_date=None) | Q(end_date__gte=timezone.now()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def board(self) -> "MembershipQuerySet":
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Filter all memberships where the user is/was in the board.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Be aware that users who were in the board in the past
 | 
				
			||||||
 | 
					        are included, even if there are no more members.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If you want to get the users who are currently in the board,
 | 
				
			||||||
 | 
					        mind combining this with the :meth:`ongoing` queryset method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # noinspection PyTypeChecker
 | 
				
			||||||
 | 
					        return self.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, **kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Work just like the default Django's update() method,
 | 
				
			||||||
 | 
					        but add a cache refresh for the elements of the queryset.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Be aware that this adds a db query to retrieve the updated objects
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        nb_rows = super().update(**kwargs)
 | 
				
			||||||
 | 
					        if nb_rows > 0:
 | 
				
			||||||
 | 
					            # if at least a row was affected, refresh the cache
 | 
				
			||||||
 | 
					            for membership in self.all():
 | 
				
			||||||
 | 
					                if membership.end_date is not None:
 | 
				
			||||||
 | 
					                    cache.set(
 | 
				
			||||||
 | 
					                        f"membership_{membership.club_id}_{membership.user_id}",
 | 
				
			||||||
 | 
					                        "not_member",
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    cache.set(
 | 
				
			||||||
 | 
					                        f"membership_{membership.club_id}_{membership.user_id}",
 | 
				
			||||||
 | 
					                        membership,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Work just like the default Django's delete() method,
 | 
				
			||||||
 | 
					        but add a cache invalidation for the elements of the queryset
 | 
				
			||||||
 | 
					        before the deletion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Be aware that this adds a db query to retrieve the deleted element.
 | 
				
			||||||
 | 
					        As this first query take place before the deletion operation,
 | 
				
			||||||
 | 
					        it will be performed even if the deletion fails.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        ids = list(self.values_list("club_id", "user_id"))
 | 
				
			||||||
 | 
					        nb_rows, _ = super().delete()
 | 
				
			||||||
 | 
					        if nb_rows > 0:
 | 
				
			||||||
 | 
					            for club_id, user_id in ids:
 | 
				
			||||||
 | 
					                cache.set(f"membership_{club_id}_{user_id}", "not_member")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Membership(models.Model):
 | 
					class Membership(models.Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    The Membership class makes the connection between User and Clubs
 | 
					    The Membership class makes the connection between User and Clubs
 | 
				
			||||||
@@ -289,6 +372,8 @@ class Membership(models.Model):
 | 
				
			|||||||
        _("description"), max_length=128, null=False, blank=True
 | 
					        _("description"), max_length=128, null=False, blank=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    objects = MembershipQuerySet.as_manager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            self.club.name
 | 
					            self.club.name
 | 
				
			||||||
@@ -303,24 +388,34 @@ class Membership(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be super edited by the given user
 | 
					        Method to see if that object can be super edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_board_member
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_edited_by(self, user, membership=None):
 | 
					    def can_be_edited_by(self, user: User) -> bool:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Method to see if that object can be edited by the given user
 | 
					        Check if that object can be edited by the given user
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if user.memberships:
 | 
					        if user.is_root or user.is_board_member:
 | 
				
			||||||
            if membership:  # This is for optimisation purpose
 | 
					            return True
 | 
				
			||||||
                ms = membership
 | 
					        membership = self.club.get_membership_for(user)
 | 
				
			||||||
            else:
 | 
					        if membership is not None and membership.role >= self.role:
 | 
				
			||||||
                ms = user.memberships.filter(club=self.club, end_date=None).first()
 | 
					            return True
 | 
				
			||||||
            return (ms and ms.role >= self.role) or user.is_in_group(
 | 
					        return False
 | 
				
			||||||
                settings.SITH_MAIN_BOARD_GROUP
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_absolute_url(self):
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
        return reverse("club:club_members", kwargs={"club_id": self.club.id})
 | 
					        return reverse("club:club_members", kwargs={"club_id": self.club_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().save(*args, **kwargs)
 | 
				
			||||||
 | 
					        if self.end_date is None:
 | 
				
			||||||
 | 
					            cache.set(f"membership_{self.club_id}_{self.user_id}", self)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            cache.set(f"membership_{self.club_id}_{self.user_id}", "not_member")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().delete(*args, **kwargs)
 | 
				
			||||||
 | 
					        cache.delete(f"membership_{self.club_id}_{self.user_id}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Mailing(models.Model):
 | 
					class Mailing(models.Model):
 | 
				
			||||||
@@ -373,14 +468,12 @@ class Mailing(models.Model):
 | 
				
			|||||||
        return self.email + "@" + settings.SITH_MAILING_DOMAIN
 | 
					        return self.email + "@" + settings.SITH_MAILING_DOMAIN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_moderate(self, user):
 | 
					    def can_moderate(self, user):
 | 
				
			||||||
        return user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        return user.is_root or user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return (
 | 
					        if user.is_anonymous:
 | 
				
			||||||
            user.is_in_group(self)
 | 
					            return False
 | 
				
			||||||
            or user.is_root
 | 
					        return user.is_root or user.is_com_admin
 | 
				
			||||||
            or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_view(self, user):
 | 
					    def can_view(self, user):
 | 
				
			||||||
        return self.club.has_rights_in_club(user)
 | 
					        return self.club.has_rights_in_club(user)
 | 
				
			||||||
@@ -388,9 +481,8 @@ class Mailing(models.Model):
 | 
				
			|||||||
    def can_be_edited_by(self, user):
 | 
					    def can_be_edited_by(self, user):
 | 
				
			||||||
        return self.club.has_rights_in_club(user)
 | 
					        return self.club.has_rights_in_club(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def delete(self):
 | 
					    def delete(self, *args, **kwargs):
 | 
				
			||||||
        for sub in self.subscriptions.all():
 | 
					        self.subscriptions.all().delete()
 | 
				
			||||||
            sub.delete()
 | 
					 | 
				
			||||||
        super(Mailing, self).delete()
 | 
					        super(Mailing, self).delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def fetch_format(self):
 | 
					    def fetch_format(self):
 | 
				
			||||||
@@ -463,10 +555,12 @@ class MailingSubscription(models.Model):
 | 
				
			|||||||
        super(MailingSubscription, self).clean()
 | 
					        super(MailingSubscription, self).clean()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            self.mailing.club.has_rights_in_club(user)
 | 
					            self.mailing.club.has_rights_in_club(user)
 | 
				
			||||||
            or user.is_root
 | 
					            or user.is_root
 | 
				
			||||||
            or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					            or self.user.is_com_admin
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_edited_by(self, user):
 | 
					    def can_be_edited_by(self, user):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,13 +13,15 @@
 | 
				
			|||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        <table>
 | 
					        <table>
 | 
				
			||||||
            <thead>
 | 
					            <thead>
 | 
				
			||||||
                <td>{% trans %}User{% endtrans %}</td>
 | 
					                <tr>
 | 
				
			||||||
                <td>{% trans %}Role{% endtrans %}</td>
 | 
					                    <td>{% trans %}User{% endtrans %}</td>
 | 
				
			||||||
                <td>{% trans %}Description{% endtrans %}</td>
 | 
					                    <td>{% trans %}Role{% endtrans %}</td>
 | 
				
			||||||
                <td>{% trans %}Since{% endtrans %}</td>
 | 
					                    <td>{% trans %}Description{% endtrans %}</td>
 | 
				
			||||||
                {% if users_old %}
 | 
					                    <td>{% trans %}Since{% endtrans %}</td>
 | 
				
			||||||
                    <td>{% trans %}Mark as old{% endtrans %}</td>
 | 
					                    {% if users_old %}
 | 
				
			||||||
                {% endif %}
 | 
					                        <td>{% trans %}Mark as old{% endtrans %}</td>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
            </thead>
 | 
					            </thead>
 | 
				
			||||||
            <tbody>
 | 
					            <tbody>
 | 
				
			||||||
            {% for m in members %}
 | 
					            {% for m in members %}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										795
									
								
								club/tests.py
									
									
									
									
									
								
							
							
						
						@@ -1,396 +1,576 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					from datetime import timedelta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from django.utils import timezone, html
 | 
					from django.utils import timezone, html
 | 
				
			||||||
 | 
					from django.utils.timezone import now, localtime
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.core.management import call_command
 | 
					from django.core.management import call_command
 | 
				
			||||||
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from core.models import User
 | 
					from core.models import User, AnonymousUser
 | 
				
			||||||
from club.models import Club, Membership, Mailing
 | 
					from club.models import Club, Membership, Mailing
 | 
				
			||||||
from club.forms import MailingForm
 | 
					from club.forms import MailingForm
 | 
				
			||||||
 | 
					from sith.settings import SITH_BAR_MANAGER, SITH_MAIN_CLUB_ID
 | 
				
			||||||
# Create your tests here.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ClubTest(TestCase):
 | 
					class ClubTest(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Set up data for test cases related to clubs and membership
 | 
				
			||||||
 | 
					    The generated dataset is the one created by the populate command,
 | 
				
			||||||
 | 
					    plus the following modifications :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - `self.club` is a dummy club recreated for each test
 | 
				
			||||||
 | 
					    - `self.club` has two board members : skia (role 3) and comptable (role 10)
 | 
				
			||||||
 | 
					    - `self.club` has one regular member : richard
 | 
				
			||||||
 | 
					    - `self.club` has one former member : sli (who had role 2)
 | 
				
			||||||
 | 
					    - None of the `self.club` members are in the AE club.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        # subscribed users - initial members
 | 
				
			||||||
 | 
					        cls.skia = User.objects.get(username="skia")
 | 
				
			||||||
 | 
					        cls.richard = User.objects.get(username="rbatsbak")
 | 
				
			||||||
 | 
					        cls.comptable = User.objects.get(username="comptable")
 | 
				
			||||||
 | 
					        cls.sli = User.objects.get(username="sli")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # subscribed users - not initial members
 | 
				
			||||||
 | 
					        cls.krophil = User.objects.get(username="krophil")
 | 
				
			||||||
 | 
					        cls.subscriber = User.objects.get(username="subscriber")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # old subscriber
 | 
				
			||||||
 | 
					        cls.old_subscriber = User.objects.get(username="old_subscriber")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # not subscribed
 | 
				
			||||||
 | 
					        cls.public = User.objects.get(username="public")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.ae = Club.objects.filter(pk=SITH_MAIN_CLUB_ID)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					        # by default, Skia is in the AE, which creates side effect
 | 
				
			||||||
        self.skia = User.objects.filter(username="skia").first()
 | 
					        self.skia.memberships.all().delete()
 | 
				
			||||||
        self.rbatsbak = User.objects.filter(username="rbatsbak").first()
 | 
					 | 
				
			||||||
        self.guy = User.objects.filter(username="guy").first()
 | 
					 | 
				
			||||||
        self.bdf = Club.objects.filter(unix_name="bdf").first()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_to_club_from_root_ok(self):
 | 
					        # create a fake club
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.club = Club.objects.create(
 | 
				
			||||||
        self.client.post(
 | 
					            name="Fake Club",
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            unix_name="fake-club",
 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
 | 
					            address="5 rue de la République, 90000 Belfort",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        response = self.client.get(
 | 
					        self.members_url = reverse(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					            "club:club_members", kwargs={"club_id": self.club.id}
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        a_month_ago = now() - timedelta(days=30)
 | 
				
			||||||
        self.assertTrue(
 | 
					        yesterday = now() - timedelta(days=1)
 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					        Membership.objects.create(
 | 
				
			||||||
            in str(response.content)
 | 
					            club=self.club, user=self.skia, start_date=a_month_ago, role=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        Membership.objects.create(club=self.club, user=self.richard, role=1)
 | 
				
			||||||
 | 
					        Membership.objects.create(
 | 
				
			||||||
 | 
					            club=self.club, user=self.comptable, start_date=a_month_ago, role=10
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_multiple_user_to_club_from_root_ok(self):
 | 
					        # sli was a member but isn't anymore
 | 
				
			||||||
 | 
					        Membership.objects.create(
 | 
				
			||||||
 | 
					            club=self.club,
 | 
				
			||||||
 | 
					            user=self.sli,
 | 
				
			||||||
 | 
					            start_date=a_month_ago,
 | 
				
			||||||
 | 
					            end_date=yesterday,
 | 
				
			||||||
 | 
					            role=2,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cache.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MembershipQuerySetTest(ClubTest):
 | 
				
			||||||
 | 
					    def test_ongoing(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the ongoing queryset method returns the memberships that
 | 
				
			||||||
 | 
					        are not ended.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        current_members = self.club.members.ongoing()
 | 
				
			||||||
 | 
					        expected = [
 | 
				
			||||||
 | 
					            self.skia.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					            self.comptable.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					            self.richard.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.assertEqual(len(current_members), len(expected))
 | 
				
			||||||
 | 
					        for member in current_members:
 | 
				
			||||||
 | 
					            self.assertIn(member, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_board(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the board queryset method returns the memberships
 | 
				
			||||||
 | 
					        of user in the club board
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        board_members = list(self.club.members.board())
 | 
				
			||||||
 | 
					        expected = [
 | 
				
			||||||
 | 
					            self.skia.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					            self.comptable.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					            # sli is no more member, but he was in the board
 | 
				
			||||||
 | 
					            self.sli.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.assertEqual(len(board_members), len(expected))
 | 
				
			||||||
 | 
					        for member in board_members:
 | 
				
			||||||
 | 
					            self.assertIn(member, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_ongoing_board(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that combining ongoing and board returns users
 | 
				
			||||||
 | 
					        who are currently board members of the club
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        members = list(self.club.members.ongoing().board())
 | 
				
			||||||
 | 
					        expected = [
 | 
				
			||||||
 | 
					            self.skia.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					            self.comptable.memberships.get(club=self.club),
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.assertEqual(len(members), len(expected))
 | 
				
			||||||
 | 
					        for member in members:
 | 
				
			||||||
 | 
					            self.assertIn(member, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_update_invalidate_cache(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the `update` queryset method properly invalidate cache
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        mem_skia = self.skia.memberships.get(club=self.club)
 | 
				
			||||||
 | 
					        cache.set(f"membership_{mem_skia.club_id}_{mem_skia.user_id}", mem_skia)
 | 
				
			||||||
 | 
					        self.skia.memberships.update(end_date=localtime(now()).date())
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            cache.get(f"membership_{mem_skia.club_id}_{mem_skia.user_id}"), "not_member"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mem_richard = self.richard.memberships.get(club=self.club)
 | 
				
			||||||
 | 
					        cache.set(
 | 
				
			||||||
 | 
					            f"membership_{mem_richard.club_id}_{mem_richard.user_id}", mem_richard
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.richard.memberships.update(role=5)
 | 
				
			||||||
 | 
					        new_mem = self.richard.memberships.get(club=self.club)
 | 
				
			||||||
 | 
					        self.assertNotEqual(new_mem, "not_member")
 | 
				
			||||||
 | 
					        self.assertEqual(new_mem.role, 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_delete_invalidate_cache(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the `delete` queryset properly invalidate cache
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mem_skia = self.skia.memberships.get(club=self.club)
 | 
				
			||||||
 | 
					        mem_comptable = self.comptable.memberships.get(club=self.club)
 | 
				
			||||||
 | 
					        cache.set(f"membership_{mem_skia.club_id}_{mem_skia.user_id}", mem_skia)
 | 
				
			||||||
 | 
					        cache.set(
 | 
				
			||||||
 | 
					            f"membership_{mem_comptable.club_id}_{mem_comptable.user_id}", mem_comptable
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # should delete the subscriptions of skia and comptable
 | 
				
			||||||
 | 
					        self.club.members.ongoing().board().delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            cache.get(f"membership_{mem_skia.club_id}_{mem_skia.user_id}"), "not_member"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            cache.get(f"membership_{mem_comptable.club_id}_{mem_comptable.user_id}"),
 | 
				
			||||||
 | 
					            "not_member",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ClubModelTest(ClubTest):
 | 
				
			||||||
 | 
					    def assert_membership_just_started(self, user: User, role: int):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Assert that the given membership is active and started today
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        membership = user.memberships.ongoing().filter(club=self.club).first()
 | 
				
			||||||
 | 
					        self.assertIsNotNone(membership)
 | 
				
			||||||
 | 
					        self.assertEqual(localtime(now()).date(), membership.start_date)
 | 
				
			||||||
 | 
					        self.assertIsNone(membership.end_date)
 | 
				
			||||||
 | 
					        self.assertEqual(membership.role, role)
 | 
				
			||||||
 | 
					        self.assertEqual(membership.club.get_membership_for(user), membership)
 | 
				
			||||||
 | 
					        member_group = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
 | 
				
			||||||
 | 
					        board_group = self.club.unix_name + settings.SITH_BOARD_SUFFIX
 | 
				
			||||||
 | 
					        self.assertTrue(user.is_in_group(name=member_group))
 | 
				
			||||||
 | 
					        self.assertTrue(user.is_in_group(name=board_group))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assert_membership_just_ended(self, user: User):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Assert that the given user have a membership which ended today
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        today = localtime(now()).date()
 | 
				
			||||||
 | 
					        self.assertIsNotNone(
 | 
				
			||||||
 | 
					            user.memberships.filter(club=self.club, end_date=today).first()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertIsNone(self.club.get_membership_for(user))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_access_unauthorized(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that users who never subscribed and anonymous users
 | 
				
			||||||
 | 
					        cannot see the page
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.post(self.members_url)
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 403)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username="public", password="plop")
 | 
				
			||||||
 | 
					        response = self.client.post(self.members_url)
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 403)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_display(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that a GET request return a page where the requested
 | 
				
			||||||
 | 
					        information are displayed.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.client.login(username=self.skia.username, password="plop")
 | 
				
			||||||
 | 
					        response = self.client.get(self.members_url)
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					        expected_html = (
 | 
				
			||||||
 | 
					            "<table><thead><tr>"
 | 
				
			||||||
 | 
					            "<td>Utilisateur</td><td>Rôle</td><td>Description</td>"
 | 
				
			||||||
 | 
					            "<td>Depuis</td><td>Marquer comme ancien</td>"
 | 
				
			||||||
 | 
					            "</tr></thead><tbody>"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        memberships = self.club.members.ongoing().order_by("-role")
 | 
				
			||||||
 | 
					        input_id = 0
 | 
				
			||||||
 | 
					        for membership in memberships.select_related("user"):
 | 
				
			||||||
 | 
					            user = membership.user
 | 
				
			||||||
 | 
					            expected_html += (
 | 
				
			||||||
 | 
					                f"<tr><td><a href=\"{reverse('core:user_profile', args=[user.id])}\">"
 | 
				
			||||||
 | 
					                f"{user.get_display_name()}</a></td>"
 | 
				
			||||||
 | 
					                f"<td>{settings.SITH_CLUB_ROLES[membership.role]}</td>"
 | 
				
			||||||
 | 
					                f"<td>{membership.description}</td>"
 | 
				
			||||||
 | 
					                f"<td>{membership.start_date}</td><td>"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            if membership.role <= 3:  # 3 is the role of skia
 | 
				
			||||||
 | 
					                expected_html += (
 | 
				
			||||||
 | 
					                    '<input type="checkbox" name="users_old" '
 | 
				
			||||||
 | 
					                    f'value="{user.id}" '
 | 
				
			||||||
 | 
					                    f'id="id_users_old_{input_id}">'
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                input_id += 1
 | 
				
			||||||
 | 
					            expected_html += "</td></tr>"
 | 
				
			||||||
 | 
					        expected_html += "</tbody></table>"
 | 
				
			||||||
 | 
					        self.assertInHTML(expected_html, response.content.decode())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_root_add_one_club_member(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that root users can add members to clubs, one at a time
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
        self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users": self.subscriber.id, "role": 3},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
 | 
					        self.subscriber.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assert_membership_just_started(self.subscriber, role=3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_root_add_multiple_club_member(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that root users can add multiple members at once to clubs
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
 | 
					                "users": f"|{self.subscriber.id}|{self.krophil.id}|",
 | 
				
			||||||
                "start_date": "12/06/2016",
 | 
					 | 
				
			||||||
                "role": 3,
 | 
					                "role": 3,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        response = self.client.get(
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					        self.subscriber.refresh_from_db()
 | 
				
			||||||
        )
 | 
					        self.assert_membership_just_started(self.subscriber, role=3)
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        self.assert_membership_just_started(self.krophil, role=3)
 | 
				
			||||||
        content = str(response.content)
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_to_club_from_root_fail_not_subscriber(self):
 | 
					    def test_add_unauthorized_members(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that users who are not currently subscribed
 | 
				
			||||||
 | 
					        cannot be members of clubs.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
        response = self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users": self.guy.id, "start_date": "12/06/2016", "role": 3},
 | 
					            {"users": self.public.id, "role": 1},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        self.assertIsNone(self.public.memberships.filter(club=self.club).first())
 | 
				
			||||||
        self.assertTrue('<ul class="errorlist"><li>' in str(response.content))
 | 
					        self.assertTrue('<ul class="errorlist"><li>' in str(response.content))
 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "Guy Carlier</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_to_club_from_root_fail_already_in_club(self):
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users": self.old_subscriber.id, "role": 1},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertIsNone(self.public.memberships.filter(club=self.club).first())
 | 
				
			||||||
 | 
					        self.assertIsNone(self.club.get_membership_for(self.public))
 | 
				
			||||||
 | 
					        self.assertTrue('<ul class="errorlist"><li>' in str(response.content))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_add_members_already_members(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that users who are already members of a club
 | 
				
			||||||
 | 
					        cannot be added again to this club
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
 | 
					        current_membership = self.skia.memberships.ongoing().get(club=self.club)
 | 
				
			||||||
 | 
					        nb_memberships = self.skia.memberships.count()
 | 
				
			||||||
        self.client.post(
 | 
					        self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
 | 
					            {"users": self.skia.id, "role": current_membership.role + 1},
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 4},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                <td>Secrétaire</td>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.skia.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(nb_memberships, self.skia.memberships.count())
 | 
				
			||||||
 | 
					        new_membership = self.skia.memberships.ongoing().get(club=self.club)
 | 
				
			||||||
 | 
					        self.assertEqual(current_membership, new_membership)
 | 
				
			||||||
 | 
					        self.assertEqual(self.club.get_membership_for(self.skia), new_membership)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_non_existent_to_club_from_root_fail(self):
 | 
					    def test_add_not_existing_users(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that not existing users cannot be added in clubs.
 | 
				
			||||||
 | 
					        If one user in the request is invalid, no membership creation at all
 | 
				
			||||||
 | 
					        can take place.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
 | 
					        nb_memberships = self.club.members.count()
 | 
				
			||||||
        response = self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users": [9999], "start_date": "12/06/2016", "role": 3},
 | 
					            {"users": [9999], "role": 1},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        self.assertContains(response, '<ul class="errorlist"><li>')
 | 
				
			||||||
        content = str(response.content)
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
        self.assertTrue('<ul class="errorlist"><li>' in content)
 | 
					        self.assertEqual(self.club.members.count(), nb_memberships)
 | 
				
			||||||
        self.assertFalse("<td>Responsable info</td>" in content)
 | 
					 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "users": "|%d|%d|" % (self.skia.id, 9999),
 | 
					                "users": f"|{self.subscriber.id}|{9999}|",
 | 
				
			||||||
                "start_date": "12/06/2016",
 | 
					                "start_date": "12/06/2016",
 | 
				
			||||||
                "role": 3,
 | 
					                "role": 3,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        self.assertContains(response, '<ul class="errorlist"><li>')
 | 
				
			||||||
        content = str(response.content)
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
        self.assertTrue('<ul class="errorlist"><li>' in content)
 | 
					        self.assertEqual(self.club.members.count(), nb_memberships)
 | 
				
			||||||
        self.assertFalse("<td>Responsable info</td>" in content)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_to_club_from_skia_ok(self):
 | 
					    def test_president_add_members(self):
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        """
 | 
				
			||||||
        self.client.post(
 | 
					        Test that the president of the club can add members
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        """
 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 10},
 | 
					        president = self.club.members.get(role=10).user
 | 
				
			||||||
 | 
					        nb_club_membership = self.club.members.count()
 | 
				
			||||||
 | 
					        nb_subscriber_memberships = self.subscriber.memberships.count()
 | 
				
			||||||
 | 
					        self.client.login(username=president.username, password="plop")
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users": self.subscriber.id, "role": 9},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.client.login(username="skia", password="plop")
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
        self.client.post(
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        self.subscriber.refresh_from_db()
 | 
				
			||||||
            {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 9},
 | 
					        self.assertEqual(self.club.members.count(), nb_club_membership + 1)
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            self.subscriber.memberships.count(), nb_subscriber_memberships + 1
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        response = self.client.get(
 | 
					        self.assert_membership_just_started(self.subscriber, role=9)
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					
 | 
				
			||||||
 | 
					    def test_add_member_greater_role(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that a member of the club member cannot create
 | 
				
			||||||
 | 
					        a membership with a greater role than its own.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.client.login(username=self.skia.username, password="plop")
 | 
				
			||||||
 | 
					        nb_memberships = self.club.members.count()
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users": self.subscriber.id, "role": 10},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
        self.assertIn(
 | 
					        self.assertInHTML(
 | 
				
			||||||
            """Richard Batsbak</a></td>\n                    <td>Vice-Président⸱e</td>""",
 | 
					            "<li>Vous n'avez pas la permission de faire cela</li>",
 | 
				
			||||||
            response.content.decode(),
 | 
					            response.content.decode(),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(nb_memberships, self.club.members.count())
 | 
				
			||||||
 | 
					        self.assertIsNone(self.subscriber.memberships.filter(club=self.club).first())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_create_add_user_to_club_from_richard_fail(self):
 | 
					    def test_add_member_without_role(self):
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        """
 | 
				
			||||||
        self.client.post(
 | 
					        Test that trying to add members without specifying their role fails
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        """
 | 
				
			||||||
            {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.client.login(username="rbatsbak", password="plop")
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 10},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "<li>Vous n'avez pas la permission de faire cela</li>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_role_required_if_users_specified(self):
 | 
					 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
        response = self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users": self.rbatsbak.id, "start_date": "12/06/2016"},
 | 
					            {"users": self.subscriber.id, "start_date": "12/06/2016"},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertTrue(
 | 
				
			||||||
            '<ul class="errorlist"><li>Vous devez choisir un r' in str(response.content)
 | 
					            '<ul class="errorlist"><li>Vous devez choisir un r' in str(response.content)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_mark_old_user_to_club_from_skia_ok(self):
 | 
					    def test_end_membership_self(self):
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        """
 | 
				
			||||||
        self.client.post(
 | 
					        Test that a member can end its own membership
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        """
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
 | 
					 | 
				
			||||||
                "start_date": "12/06/2016",
 | 
					 | 
				
			||||||
                "role": 3,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.client.login(username="skia", password="plop")
 | 
					        self.client.login(username="skia", password="plop")
 | 
				
			||||||
        response = self.client.post(
 | 
					        self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users_old": self.rbatsbak.id},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 302)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
        content = str(response.content)
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Skia is board member so he should be able to mark as old even without being in the club
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users_old": self.skia.id},
 | 
					            {"users_old": self.skia.id},
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.skia.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assert_membership_just_ended(self.skia)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_end_membership_lower_role(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that board members of the club can end memberships
 | 
				
			||||||
 | 
					        of users with lower roles
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # remainder : skia has role 3, comptable has role 10, richard has role 1
 | 
				
			||||||
 | 
					        self.client.login(username=self.skia.username, password="plop")
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users_old": self.richard.id},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assert_membership_just_ended(self.richard)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_end_membership_higher_role(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that board members of the club cannot end memberships
 | 
				
			||||||
 | 
					        of users with higher roles
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        membership = self.comptable.memberships.filter(club=self.club).first()
 | 
				
			||||||
 | 
					        self.client.login(username=self.skia.username, password="plop")
 | 
				
			||||||
 | 
					        self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users_old": self.comptable.id},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.club.refresh_from_db()
 | 
				
			||||||
 | 
					        new_membership = self.club.get_membership_for(self.comptable)
 | 
				
			||||||
 | 
					        self.assertIsNotNone(new_membership)
 | 
				
			||||||
 | 
					        self.assertEqual(new_membership, membership)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        membership = self.comptable.memberships.filter(club=self.club).first()
 | 
				
			||||||
 | 
					        self.assertIsNone(membership.end_date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_end_membership_as_main_club_board(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that board members of the main club can end the membership
 | 
				
			||||||
 | 
					        of anyone
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # make subscriber a board member
 | 
				
			||||||
 | 
					        self.subscriber.memberships.all().delete()
 | 
				
			||||||
 | 
					        Membership.objects.create(club=self.ae, user=self.subscriber, role=3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nb_memberships = self.club.members.count()
 | 
				
			||||||
 | 
					        self.client.login(username=self.subscriber.username, password="plop")
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.members_url,
 | 
				
			||||||
 | 
					            {"users_old": self.comptable.id},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
 | 
					        self.assert_membership_just_ended(self.comptable)
 | 
				
			||||||
 | 
					        self.assertEqual(self.club.members.ongoing().count(), nb_memberships - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_end_membership_as_root(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that root users can end the membership of anyone
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        nb_memberships = self.club.members.count()
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        self.client.login(username="root", password="plop")
 | 
				
			||||||
        self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 3},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.client.login(username="skia", password="plop")
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					        response = self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {"users_old": self.rbatsbak.id},
 | 
					            {"users_old": [self.comptable.id]},
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.members_url)
 | 
				
			||||||
 | 
					        self.assert_membership_just_ended(self.comptable)
 | 
				
			||||||
 | 
					        self.assertEqual(self.club.members.ongoing().count(), nb_memberships - 1)
 | 
				
			||||||
 | 
					        self.assertEqual(self.club.members.count(), nb_memberships)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_mark_old_multiple_users_from_skia_ok(self):
 | 
					    def test_end_membership_as_foreigner(self):
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        """
 | 
				
			||||||
 | 
					        Test that users who are not in this club cannot end its memberships
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        nb_memberships = self.club.members.count()
 | 
				
			||||||
 | 
					        membership = self.richard.memberships.filter(club=self.club).first()
 | 
				
			||||||
 | 
					        self.client.login(username="subscriber", password="root")
 | 
				
			||||||
        self.client.post(
 | 
					        self.client.post(
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					            self.members_url,
 | 
				
			||||||
            {
 | 
					            {"users_old": [self.richard.id]},
 | 
				
			||||||
                "users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
 | 
					 | 
				
			||||||
                "start_date": "12/06/2016",
 | 
					 | 
				
			||||||
                "role": 3,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.client.login(username="skia", password="plop")
 | 
					        # nothing should have changed
 | 
				
			||||||
        response = self.client.post(
 | 
					        new_mem = self.club.get_membership_for(self.richard)
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        self.assertIsNotNone(new_mem)
 | 
				
			||||||
            {"users_old": [self.rbatsbak.id, self.skia.id]},
 | 
					        self.assertEqual(self.club.members.count(), nb_memberships)
 | 
				
			||||||
        )
 | 
					        self.assertEqual(membership, new_mem)
 | 
				
			||||||
        self.assertTrue(response.status_code == 302)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = self.client.get(
 | 
					    def test_delete_remove_from_meta_group(self):
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					        """
 | 
				
			||||||
        )
 | 
					        Test that when a club is deleted, all its members are removed from the
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        associated metagroup
 | 
				
			||||||
        content = str(response.content)
 | 
					        """
 | 
				
			||||||
        self.assertFalse(
 | 
					        memberships = self.club.members.select_related("user")
 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					        users = [membership.user for membership in memberships]
 | 
				
			||||||
            in content
 | 
					        meta_group = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_mark_old_user_to_club_from_richard_ok(self):
 | 
					        self.club.delete()
 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					        for user in users:
 | 
				
			||||||
        self.client.post(
 | 
					            self.assertFalse(user.is_in_group(name=meta_group))
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "users": "|%d|%d|" % (self.skia.id, self.rbatsbak.id),
 | 
					 | 
				
			||||||
                "start_date": "12/06/2016",
 | 
					 | 
				
			||||||
                "role": 3,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test with equal rights
 | 
					    def test_add_to_meta_group(self):
 | 
				
			||||||
        self.client.login(username="rbatsbak", password="plop")
 | 
					        """
 | 
				
			||||||
        response = self.client.post(
 | 
					        Test that when a membership begins, the user is added to the meta group
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        """
 | 
				
			||||||
            {"users_old": self.skia.id},
 | 
					        group_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
 | 
				
			||||||
        )
 | 
					        board_members = self.club.unix_name + settings.SITH_BOARD_SUFFIX
 | 
				
			||||||
        self.assertTrue(response.status_code == 302)
 | 
					        self.assertFalse(self.subscriber.is_in_group(name=group_members))
 | 
				
			||||||
 | 
					        self.assertFalse(self.subscriber.is_in_group(name=board_members))
 | 
				
			||||||
 | 
					        Membership.objects.create(club=self.club, user=self.subscriber, role=3)
 | 
				
			||||||
 | 
					        self.assertTrue(self.subscriber.is_in_group(name=group_members))
 | 
				
			||||||
 | 
					        self.assertTrue(self.subscriber.is_in_group(name=board_members))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = self.client.get(
 | 
					    def test_remove_from_meta_group(self):
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					        """
 | 
				
			||||||
        )
 | 
					        Test that when a membership ends, the user is removed from meta group
 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					        """
 | 
				
			||||||
        content = str(response.content)
 | 
					        group_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
 | 
				
			||||||
        self.assertTrue(
 | 
					        board_members = self.club.unix_name + settings.SITH_BOARD_SUFFIX
 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					        self.assertTrue(self.comptable.is_in_group(name=group_members))
 | 
				
			||||||
            in content
 | 
					        self.assertTrue(self.comptable.is_in_group(name=board_members))
 | 
				
			||||||
        )
 | 
					        self.comptable.memberships.update(end_date=localtime(now()))
 | 
				
			||||||
        self.assertFalse(
 | 
					        self.assertFalse(self.comptable.is_in_group(name=group_members))
 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					        self.assertFalse(self.comptable.is_in_group(name=board_members))
 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test with lower rights
 | 
					    def test_club_owner(self):
 | 
				
			||||||
        self.client.post(
 | 
					        """
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        Test that a club is owned only by board members of the main club
 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 0},
 | 
					        """
 | 
				
			||||||
        )
 | 
					        anonymous = AnonymousUser()
 | 
				
			||||||
 | 
					        self.assertFalse(self.club.is_owned_by(anonymous))
 | 
				
			||||||
 | 
					        self.assertFalse(self.club.is_owned_by(self.subscriber))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.client.post(
 | 
					        # make sli a board member
 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					        self.sli.memberships.all().delete()
 | 
				
			||||||
            {"users_old": self.skia.id},
 | 
					        Membership(club=self.ae, user=self.sli, role=3).save()
 | 
				
			||||||
        )
 | 
					        self.assertTrue(self.club.is_owned_by(self.sli))
 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
        content = str(response.content)
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "Richard Batsbak</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Curieux</td>" in content
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_mark_old_user_to_club_from_richard_fail(self):
 | 
					 | 
				
			||||||
        self.client.login(username="root", password="plop")
 | 
					 | 
				
			||||||
        self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users": self.skia.id, "start_date": "12/06/2016", "role": 3},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Test with richard outside of the club
 | 
					 | 
				
			||||||
        self.client.login(username="rbatsbak", password="plop")
 | 
					 | 
				
			||||||
        response = self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users_old": self.skia.id},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(response.status_code == 200)
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\\n                    <td>Responsable info</td>"
 | 
					 | 
				
			||||||
            in str(response.content)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Test with lower rights
 | 
					 | 
				
			||||||
        self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users": self.rbatsbak.id, "start_date": "12/06/2016", "role": 0},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client.post(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id}),
 | 
					 | 
				
			||||||
            {"users_old": self.skia.id},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        response = self.client.get(
 | 
					 | 
				
			||||||
            reverse("club:club_members", kwargs={"club_id": self.bdf.id})
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertEqual(response.status_code, 200)
 | 
					 | 
				
			||||||
        content = response.content.decode()
 | 
					 | 
				
			||||||
        self.assertIn(
 | 
					 | 
				
			||||||
            "Richard Batsbak</a></td>\n                    <td>Curieux⸱euse</td>",
 | 
					 | 
				
			||||||
            content,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertIn(
 | 
					 | 
				
			||||||
            "S' Kia</a></td>\n                    <td>Responsable info</td>",
 | 
					 | 
				
			||||||
            content,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MailingFormTest(TestCase):
 | 
					class MailingFormTest(TestCase):
 | 
				
			||||||
    """Perform validation tests for MailingForm"""
 | 
					    """Perform validation tests for MailingForm"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        cls.skia = User.objects.filter(username="skia").first()
 | 
				
			||||||
 | 
					        cls.rbatsbak = User.objects.filter(username="rbatsbak").first()
 | 
				
			||||||
 | 
					        cls.krophil = User.objects.filter(username="krophil").first()
 | 
				
			||||||
 | 
					        cls.comunity = User.objects.filter(username="comunity").first()
 | 
				
			||||||
 | 
					        cls.bdf = Club.objects.filter(unix_name=SITH_BAR_MANAGER["unix_name"]).first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
        self.skia = User.objects.filter(username="skia").first()
 | 
					 | 
				
			||||||
        self.rbatsbak = User.objects.filter(username="rbatsbak").first()
 | 
					 | 
				
			||||||
        self.krophil = User.objects.filter(username="krophil").first()
 | 
					 | 
				
			||||||
        self.comunity = User.objects.filter(username="comunity").first()
 | 
					 | 
				
			||||||
        self.bdf = Club.objects.filter(unix_name="bdf").first()
 | 
					 | 
				
			||||||
        Membership(
 | 
					        Membership(
 | 
				
			||||||
            user=self.rbatsbak,
 | 
					            user=self.rbatsbak,
 | 
				
			||||||
            club=self.bdf,
 | 
					            club=self.bdf,
 | 
				
			||||||
@@ -705,7 +885,6 @@ class ClubSellingViewTest(TestCase):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
        self.ae = Club.objects.filter(unix_name="ae").first()
 | 
					        self.ae = Club.objects.filter(unix_name="ae").first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_page_not_internal_error(self):
 | 
					    def test_page_not_internal_error(self):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								club/urls.py
									
									
									
									
									
								
							
							
						
						@@ -23,94 +23,84 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.urls import re_path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from club.views import *
 | 
					from club.views import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    re_path(r"^$", ClubListView.as_view(), name="club_list"),
 | 
					    path("", ClubListView.as_view(), name="club_list"),
 | 
				
			||||||
    re_path(r"^new$", ClubCreateView.as_view(), name="club_new"),
 | 
					    path("new/", ClubCreateView.as_view(), name="club_new"),
 | 
				
			||||||
    re_path(r"^stats$", ClubStatView.as_view(), name="club_stats"),
 | 
					    path("stats/", ClubStatView.as_view(), name="club_stats"),
 | 
				
			||||||
    re_path(r"^(?P<club_id>[0-9]+)/$", ClubView.as_view(), name="club_view"),
 | 
					    path("<int:club_id>/", ClubView.as_view(), name="club_view"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/rev/(?P<rev_id>[0-9]+)/$",
 | 
					        "<int:club_id>/rev/<int:rev_id>/",
 | 
				
			||||||
        ClubRevView.as_view(),
 | 
					        ClubRevView.as_view(),
 | 
				
			||||||
        name="club_view_rev",
 | 
					        name="club_view_rev",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("<int:club_id>/hist/", ClubPageHistView.as_view(), name="club_hist"),
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/hist$", ClubPageHistView.as_view(), name="club_hist"
 | 
					    path("<int:club_id>/edit/", ClubEditView.as_view(), name="club_edit"),
 | 
				
			||||||
    ),
 | 
					    path(
 | 
				
			||||||
    re_path(r"^(?P<club_id>[0-9]+)/edit$", ClubEditView.as_view(), name="club_edit"),
 | 
					        "<int:club_id>/edit/page/",
 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/edit/page$",
 | 
					 | 
				
			||||||
        ClubPageEditView.as_view(),
 | 
					        ClubPageEditView.as_view(),
 | 
				
			||||||
        name="club_edit_page",
 | 
					        name="club_edit_page",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("<int:club_id>/members/", ClubMembersView.as_view(), name="club_members"),
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/members$", ClubMembersView.as_view(), name="club_members"
 | 
					    path(
 | 
				
			||||||
    ),
 | 
					        "<int:club_id>/elderlies/",
 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/elderlies$",
 | 
					 | 
				
			||||||
        ClubOldMembersView.as_view(),
 | 
					        ClubOldMembersView.as_view(),
 | 
				
			||||||
        name="club_old_members",
 | 
					        name="club_old_members",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/sellings$",
 | 
					        "<int:club_id>/sellings/",
 | 
				
			||||||
        ClubSellingView.as_view(),
 | 
					        ClubSellingView.as_view(),
 | 
				
			||||||
        name="club_sellings",
 | 
					        name="club_sellings",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/sellings/csv$",
 | 
					        "<int:club_id>/sellings/csv/",
 | 
				
			||||||
        ClubSellingCSVView.as_view(),
 | 
					        ClubSellingCSVView.as_view(),
 | 
				
			||||||
        name="sellings_csv",
 | 
					        name="sellings_csv",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("<int:club_id>/prop/", ClubEditPropView.as_view(), name="club_prop"),
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/prop$", ClubEditPropView.as_view(), name="club_prop"
 | 
					    path("<int:club_id>/tools/", ClubToolsView.as_view(), name="tools"),
 | 
				
			||||||
    ),
 | 
					    path("<int:club_id>/mailing/", ClubMailingView.as_view(), name="mailing"),
 | 
				
			||||||
    re_path(r"^(?P<club_id>[0-9]+)/tools$", ClubToolsView.as_view(), name="tools"),
 | 
					    path(
 | 
				
			||||||
    re_path(
 | 
					        "<int:mailing_id>/mailing/generate/",
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/mailing$", ClubMailingView.as_view(), name="mailing"
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^(?P<mailing_id>[0-9]+)/mailing/generate$",
 | 
					 | 
				
			||||||
        MailingAutoGenerationView.as_view(),
 | 
					        MailingAutoGenerationView.as_view(),
 | 
				
			||||||
        name="mailing_generate",
 | 
					        name="mailing_generate",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<mailing_id>[0-9]+)/mailing/delete$",
 | 
					        "<int:mailing_id>/mailing/delete/",
 | 
				
			||||||
        MailingDeleteView.as_view(),
 | 
					        MailingDeleteView.as_view(),
 | 
				
			||||||
        name="mailing_delete",
 | 
					        name="mailing_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<mailing_subscription_id>[0-9]+)/mailing/delete/subscription$",
 | 
					        "<int:mailing_subscription_id>/mailing/delete/subscription/",
 | 
				
			||||||
        MailingSubscriptionDeleteView.as_view(),
 | 
					        MailingSubscriptionDeleteView.as_view(),
 | 
				
			||||||
        name="mailing_subscription_delete",
 | 
					        name="mailing_subscription_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^membership/(?P<membership_id>[0-9]+)/set_old$",
 | 
					        "membership/<int:membership_id>/set_old/",
 | 
				
			||||||
        MembershipSetOldView.as_view(),
 | 
					        MembershipSetOldView.as_view(),
 | 
				
			||||||
        name="membership_set_old",
 | 
					        name="membership_set_old",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^membership/(?P<membership_id>[0-9]+)/delete$",
 | 
					        "membership/<int:membership_id>/delete/",
 | 
				
			||||||
        MembershipDeleteView.as_view(),
 | 
					        MembershipDeleteView.as_view(),
 | 
				
			||||||
        name="membership_delete",
 | 
					        name="membership_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("<int:club_id>/poster/", PosterListView.as_view(), name="poster_list"),
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/poster$", PosterListView.as_view(), name="poster_list"
 | 
					    path(
 | 
				
			||||||
    ),
 | 
					        "<int:club_id>/poster/create/",
 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/poster/create$",
 | 
					 | 
				
			||||||
        PosterCreateView.as_view(),
 | 
					        PosterCreateView.as_view(),
 | 
				
			||||||
        name="poster_create",
 | 
					        name="poster_create",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/edit$",
 | 
					        "<int:club_id>/poster/<int:poster_id>/edit/",
 | 
				
			||||||
        PosterEditView.as_view(),
 | 
					        PosterEditView.as_view(),
 | 
				
			||||||
        name="poster_edit",
 | 
					        name="poster_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/delete$",
 | 
					        "<int:club_id>/poster/<int:poster_id>/delete/",
 | 
				
			||||||
        PosterDeleteView.as_view(),
 | 
					        PosterDeleteView.as_view(),
 | 
				
			||||||
        name="poster_delete",
 | 
					        name="poster_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -306,9 +306,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
 | 
				
			|||||||
        return resp
 | 
					        return resp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
        self.members = (
 | 
					        self.members = self.get_object().members.ongoing().order_by("-role")
 | 
				
			||||||
            self.get_object().members.filter(end_date=None).order_by("-role").all()
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return super(ClubMembersView, self).dispatch(request, *args, **kwargs)
 | 
					        return super(ClubMembersView, self).dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_success_url(self, **kwargs):
 | 
					    def get_success_url(self, **kwargs):
 | 
				
			||||||
@@ -443,7 +441,6 @@ class ClubSellingCSVView(ClubSellingView):
 | 
				
			|||||||
        return row
 | 
					        return row
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, request, *args, **kwargs):
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.object = self.get_object()
 | 
					        self.object = self.get_object()
 | 
				
			||||||
        kwargs = self.get_context_data(**kwargs)
 | 
					        kwargs = self.get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -706,7 +703,6 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MailingDeleteView(CanEditMixin, DeleteView):
 | 
					class MailingDeleteView(CanEditMixin, DeleteView):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Mailing
 | 
					    model = Mailing
 | 
				
			||||||
    template_name = "core/delete_confirm.jinja"
 | 
					    template_name = "core/delete_confirm.jinja"
 | 
				
			||||||
    pk_url_kwarg = "mailing_id"
 | 
					    pk_url_kwarg = "mailing_id"
 | 
				
			||||||
@@ -724,7 +720,6 @@ class MailingDeleteView(CanEditMixin, DeleteView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MailingSubscriptionDeleteView(CanEditMixin, DeleteView):
 | 
					class MailingSubscriptionDeleteView(CanEditMixin, DeleteView):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = MailingSubscription
 | 
					    model = MailingSubscription
 | 
				
			||||||
    template_name = "core/delete_confirm.jinja"
 | 
					    template_name = "core/delete_confirm.jinja"
 | 
				
			||||||
    pk_url_kwarg = "mailing_subscription_id"
 | 
					    pk_url_kwarg = "mailing_subscription_id"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										50
									
								
								com/admin.py
									
									
									
									
									
								
							
							
						
						@@ -1,43 +1,49 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					from ajax_select import make_ajax_form
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from haystack.admin import SearchModelAdmin
 | 
					from haystack.admin import SearchModelAdmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from com.models import *
 | 
					from com.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(News)
 | 
				
			||||||
class NewsAdmin(SearchModelAdmin):
 | 
					class NewsAdmin(SearchModelAdmin):
 | 
				
			||||||
    search_fields = ["title", "summary", "content"]
 | 
					    list_display = ("title", "type", "club", "author")
 | 
				
			||||||
 | 
					    search_fields = ("title", "summary", "content")
 | 
				
			||||||
 | 
					    form = make_ajax_form(
 | 
				
			||||||
 | 
					        News,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "author": "users",
 | 
				
			||||||
 | 
					            "moderator": "users",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Poster)
 | 
				
			||||||
 | 
					class PosterAdmin(SearchModelAdmin):
 | 
				
			||||||
 | 
					    list_display = ("name", "club", "date_begin", "date_end", "moderator")
 | 
				
			||||||
 | 
					    form = make_ajax_form(Poster, {"moderator": "users"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Weekmail)
 | 
				
			||||||
class WeekmailAdmin(SearchModelAdmin):
 | 
					class WeekmailAdmin(SearchModelAdmin):
 | 
				
			||||||
    search_fields = ["title"]
 | 
					    list_display = ("title", "sent")
 | 
				
			||||||
 | 
					    search_fields = ("title",)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
admin.site.register(Sith)
 | 
					admin.site.register(Sith)
 | 
				
			||||||
admin.site.register(News, NewsAdmin)
 | 
					 | 
				
			||||||
admin.site.register(Weekmail, WeekmailAdmin)
 | 
					 | 
				
			||||||
admin.site.register(Screen)
 | 
					admin.site.register(Screen)
 | 
				
			||||||
admin.site.register(Poster)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = []
 | 
					    dependencies = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        ("club", "0005_auto_20161120_1149"),
 | 
					        ("club", "0005_auto_20161120_1149"),
 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        ("club", "0006_auto_20161229_0040"),
 | 
					        ("club", "0006_auto_20161229_0040"),
 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@ import django.db.models.deletion
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					    dependencies = [
 | 
				
			||||||
        ("club", "0010_auto_20170912_2028"),
 | 
					        ("club", "0010_auto_20170912_2028"),
 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ from django.db import migrations, models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("com", "0004_auto_20171221_1614")]
 | 
					    dependencies = [("com", "0004_auto_20171221_1614")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [
 | 
					    operations = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ from django.db import migrations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [("com", "0005_auto_20180318_2227")]
 | 
					    dependencies = [("com", "0005_auto_20180318_2227")]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    operations = [migrations.RemoveField(model_name="sith", name="index_page")]
 | 
					    operations = [migrations.RemoveField(model_name="sith", name="index_page")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ from django.core.exceptions import ValidationError
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from core import utils
 | 
				
			||||||
from core.models import User, Preferences, RealGroup, Notification, SithFile
 | 
					from core.models import User, Preferences, RealGroup, Notification, SithFile
 | 
				
			||||||
from club.models import Club
 | 
					from club.models import Club
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,9 +47,12 @@ class Sith(models.Model):
 | 
				
			|||||||
    alert_msg = models.TextField(_("alert message"), default="", blank=True)
 | 
					    alert_msg = models.TextField(_("alert message"), default="", blank=True)
 | 
				
			||||||
    info_msg = models.TextField(_("info message"), default="", blank=True)
 | 
					    info_msg = models.TextField(_("info message"), default="", blank=True)
 | 
				
			||||||
    weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
 | 
					    weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
 | 
				
			||||||
 | 
					    version = utils.get_git_revision_short_hash()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return "⛩ Sith ⛩"
 | 
					        return "⛩ Sith ⛩"
 | 
				
			||||||
@@ -90,13 +94,15 @@ class News(models.Model):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user == self.author
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_com_admin or user == self.author
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_edited_by(self, user):
 | 
					    def can_be_edited_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_viewed_by(self, user):
 | 
					    def can_be_viewed_by(self, user):
 | 
				
			||||||
        return self.is_moderated or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        return self.is_moderated or user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_absolute_url(self):
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
        return reverse("com:news_detail", kwargs={"news_id": self.id})
 | 
					        return reverse("com:news_detail", kwargs={"news_id": self.id})
 | 
				
			||||||
@@ -228,7 +234,7 @@ class Weekmail(models.Model):
 | 
				
			|||||||
        Return an absolute link to the banner.
 | 
					        Return an absolute link to the banner.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            "http://" + settings.SITH_URL + static("com/img/invitation_bannerP22.png")
 | 
					            "http://" + settings.SITH_URL + static("com/img/weekmail_bannerV2P22.png")
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_footer(self):
 | 
					    def get_footer(self):
 | 
				
			||||||
@@ -241,7 +247,9 @@ class Weekmail(models.Model):
 | 
				
			|||||||
        return "Weekmail %s (sent: %s) - %s" % (self.id, self.sent, self.title)
 | 
					        return "Weekmail %s (sent: %s) - %s" % (self.id, self.sent, self.title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WeekmailArticle(models.Model):
 | 
					class WeekmailArticle(models.Model):
 | 
				
			||||||
@@ -269,7 +277,9 @@ class WeekmailArticle(models.Model):
 | 
				
			|||||||
    rank = models.IntegerField(_("rank"), default=-1)
 | 
					    rank = models.IntegerField(_("rank"), default=-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return "%s - %s (%s)" % (self.title, self.author, self.club)
 | 
					        return "%s - %s (%s)" % (self.title, self.author, self.club)
 | 
				
			||||||
@@ -285,7 +295,9 @@ class Screen(models.Model):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        if user.is_anonymous:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return "%s" % (self.name)
 | 
					        return "%s" % (self.name)
 | 
				
			||||||
@@ -338,12 +350,12 @@ class Poster(models.Model):
 | 
				
			|||||||
            raise ValidationError(_("Begin date should be before end date"))
 | 
					            raise ValidationError(_("Begin date should be before end date"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_owned_by(self, user):
 | 
					    def is_owned_by(self, user):
 | 
				
			||||||
        return user.is_in_group(
 | 
					        if user.is_anonymous:
 | 
				
			||||||
            settings.SITH_GROUP_COM_ADMIN_ID
 | 
					            return False
 | 
				
			||||||
        ) or Club.objects.filter(id__in=user.clubs_with_rights)
 | 
					        return user.is_com_admin or len(user.clubs_with_rights) > 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_be_moderated_by(self, user):
 | 
					    def can_be_moderated_by(self, user):
 | 
				
			||||||
        return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					        return user.is_com_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_display_name(self):
 | 
					    def get_display_name(self):
 | 
				
			||||||
        return self.club.get_display_name()
 | 
					        return self.club.get_display_name()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
        <p>{% trans %}Author: {% endtrans %}{{ user_profile_link(news.author) }}</p>
 | 
					        <p>{% trans %}Author: {% endtrans %}{{ user_profile_link(news.author) }}</p>
 | 
				
			||||||
        {% if news.moderator %}
 | 
					        {% if news.moderator %}
 | 
				
			||||||
        <p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
 | 
					        <p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
 | 
				
			||||||
        {% elif user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
 | 
					        {% elif user.is_com_admin %}
 | 
				
			||||||
        <p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
 | 
					        <p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        {% if user.can_edit(news) %}
 | 
					        {% if user.can_edit(news) %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@
 | 
				
			|||||||
    <p>{{ form.club.errors }}<label for="{{ form.club.name }}">{{ form.club.label }}</label> {{ form.club }}</p>
 | 
					    <p>{{ form.club.errors }}<label for="{{ form.club.name }}">{{ form.club.label }}</label> {{ form.club }}</p>
 | 
				
			||||||
    <p>{{ form.summary.errors }}<label for="{{ form.summary.name }}">{{ form.summary.label }}</label> {{ form.summary }}</p>
 | 
					    <p>{{ form.summary.errors }}<label for="{{ form.summary.name }}">{{ form.summary.label }}</label> {{ form.summary }}</p>
 | 
				
			||||||
    <p>{{ form.content.errors }}<label for="{{ form.content.name }}">{{ form.content.label }}</label> {{ form.content }}</p>
 | 
					    <p>{{ form.content.errors }}<label for="{{ form.content.name }}">{{ form.content.label }}</label> {{ form.content }}</p>
 | 
				
			||||||
    {% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
 | 
					    {% if user.is_com_admin %}
 | 
				
			||||||
    <p>{{ form.automoderation.errors }}<label for="{{ form.automoderation.name }}">{{ form.automoderation.label }}</label>
 | 
					    <p>{{ form.automoderation.errors }}<label for="{{ form.automoderation.name }}">{{ form.automoderation.label }}</label>
 | 
				
			||||||
        {{ form.automoderation }}</p>
 | 
					        {{ form.automoderation }}</p>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,15 +6,15 @@
 | 
				
			|||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
{% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) %}
 | 
					{% if user.is_com_admin %}
 | 
				
			||||||
<div id="news_admin">
 | 
					<div id="news_admin">
 | 
				
			||||||
  <a href="{{ url('com:news_admin_list') }}">{% trans %}Administrate news{% endtrans %}</a>
 | 
					  <a class="button" href="{{ url('com:news_admin_list') }}">{% trans %}Administrate news{% endtrans %}</a>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					<br>
 | 
				
			||||||
{% endif  %}
 | 
					{% endif  %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div id="news">
 | 
					<div id="news">
 | 
				
			||||||
    <div id="left_column" class="news_column">
 | 
					    <div id="left_column" class="news_column">
 | 
				
			||||||
 | 
					 | 
				
			||||||
        {% for news in object_list.filter(type="NOTICE") %}
 | 
					        {% for news in object_list.filter(type="NOTICE") %}
 | 
				
			||||||
            <section class="news_notice">
 | 
					            <section class="news_notice">
 | 
				
			||||||
                <h4><a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
 | 
					                <h4><a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
 | 
				
			||||||
@@ -97,6 +97,15 @@
 | 
				
			|||||||
                </section>
 | 
					                </section>
 | 
				
			||||||
            {% endfor %}
 | 
					            {% endfor %}
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <h3>{% trans %}All coming events{% endtrans %}</h3>
 | 
				
			||||||
 | 
					        <iframe 
 | 
				
			||||||
 | 
					            src="https://embed.styledcalendar.com/#2mF2is8CEXhr4ADcX6qN" 
 | 
				
			||||||
 | 
					            title="Styled Calendar"
 | 
				
			||||||
 | 
					            class="styled-calendar-container" 
 | 
				
			||||||
 | 
					            style="width: 100%; border: none; height: 1060px" 
 | 
				
			||||||
 | 
					            data-cy="calendar-embed-iframe">
 | 
				
			||||||
 | 
					        </iframe>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div id="right_column" class="news_column">
 | 
					    <div id="right_column" class="news_column">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@
 | 
				
			|||||||
        <div id="progress_bar"></div>
 | 
					        <div id="progress_bar"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script>
 | 
					    <script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script>
 | 
				
			||||||
    <script src="{{ static('com/js/slideshow.js') }}"></script>
 | 
					    <script src="{{ static('com/js/slideshow.js') }}"></script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										155
									
								
								com/tests.py
									
									
									
									
									
								
							
							
						
						@@ -1,42 +1,33 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					from django.core.files.uploadedfile import SimpleUploadedFile
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.core.management import call_command
 | 
					from django.core.management import call_command
 | 
				
			||||||
from django.utils import html
 | 
					from django.utils import html
 | 
				
			||||||
 | 
					from django.utils.timezone import localtime, now
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from club.models import Club, Membership
 | 
				
			||||||
from core.models import User, RealGroup
 | 
					from com.models import Sith, News, Weekmail, WeekmailArticle, Poster
 | 
				
			||||||
 | 
					from core.models import User, RealGroup, AnonymousUser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ComAlertTest(TestCase):
 | 
					class ComAlertTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_page_is_working(self):
 | 
					    def test_page_is_working(self):
 | 
				
			||||||
        self.client.login(username="comunity", password="plop")
 | 
					        self.client.login(username="comunity", password="plop")
 | 
				
			||||||
        response = self.client.get(reverse("com:alert_edit"))
 | 
					        response = self.client.get(reverse("com:alert_edit"))
 | 
				
			||||||
@@ -45,9 +36,6 @@ class ComAlertTest(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ComInfoTest(TestCase):
 | 
					class ComInfoTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					 | 
				
			||||||
        call_command("populate")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_page_is_working(self):
 | 
					    def test_page_is_working(self):
 | 
				
			||||||
        self.client.login(username="comunity", password="plop")
 | 
					        self.client.login(username="comunity", password="plop")
 | 
				
			||||||
        response = self.client.get(reverse("com:info_edit"))
 | 
					        response = self.client.get(reverse("com:info_edit"))
 | 
				
			||||||
@@ -56,14 +44,16 @@ class ComInfoTest(TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ComTest(TestCase):
 | 
					class ComTest(TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    @classmethod
 | 
				
			||||||
        call_command("populate")
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
        self.skia = User.objects.filter(username="skia").first()
 | 
					        cls.skia = User.objects.filter(username="skia").first()
 | 
				
			||||||
        self.com_group = RealGroup.objects.filter(
 | 
					        cls.com_group = RealGroup.objects.filter(
 | 
				
			||||||
            id=settings.SITH_GROUP_COM_ADMIN_ID
 | 
					            id=settings.SITH_GROUP_COM_ADMIN_ID
 | 
				
			||||||
        ).first()
 | 
					        ).first()
 | 
				
			||||||
        self.skia.groups.set([self.com_group])
 | 
					        cls.skia.groups.set([cls.com_group])
 | 
				
			||||||
        self.skia.save()
 | 
					        cls.skia.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
        self.client.login(username=self.skia.username, password="plop")
 | 
					        self.client.login(username=self.skia.username, password="plop")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_alert_msg(self):
 | 
					    def test_alert_msg(self):
 | 
				
			||||||
@@ -82,7 +72,7 @@ class ComTest(TestCase):
 | 
				
			|||||||
        self.assertContains(
 | 
					        self.assertContains(
 | 
				
			||||||
            r,
 | 
					            r,
 | 
				
			||||||
            """<div id="alert_box">
 | 
					            """<div id="alert_box">
 | 
				
			||||||
                    <div class="markdown"><h3>ALERTE!</h3>
 | 
					                            <div class="markdown"><h3>ALERTE!</h3>
 | 
				
			||||||
<p><strong>Caaaataaaapuuuulte!!!!</strong></p>""",
 | 
					<p><strong>Caaaataaaapuuuulte!!!!</strong></p>""",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,7 +90,7 @@ class ComTest(TestCase):
 | 
				
			|||||||
        self.assertContains(
 | 
					        self.assertContains(
 | 
				
			||||||
            r,
 | 
					            r,
 | 
				
			||||||
            """<div id="info_box">
 | 
					            """<div id="info_box">
 | 
				
			||||||
                    <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""",
 | 
					                            <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_birthday_non_subscribed_user(self):
 | 
					    def test_birthday_non_subscribed_user(self):
 | 
				
			||||||
@@ -122,3 +112,102 @@ class ComTest(TestCase):
 | 
				
			|||||||
                _("You need an up to date subscription to access this content")
 | 
					                _("You need an up to date subscription to access this content")
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SithTest(TestCase):
 | 
				
			||||||
 | 
					    def test_sith_owner(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that the sith instance is owned by com admins
 | 
				
			||||||
 | 
					        and nobody else
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        sith: Sith = Sith.objects.first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        com_admin = User.objects.get(username="comunity")
 | 
				
			||||||
 | 
					        self.assertTrue(sith.is_owned_by(com_admin))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        anonymous = AnonymousUser()
 | 
				
			||||||
 | 
					        self.assertFalse(sith.is_owned_by(anonymous))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sli = User.objects.get(username="sli")
 | 
				
			||||||
 | 
					        self.assertFalse(sith.is_owned_by(sli))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewsTest(TestCase):
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        cls.com_admin = User.objects.get(username="comunity")
 | 
				
			||||||
 | 
					        new = News.objects.create(
 | 
				
			||||||
 | 
					            title="dummy new",
 | 
				
			||||||
 | 
					            summary="This is a dummy new",
 | 
				
			||||||
 | 
					            content="Look at that beautiful dummy new",
 | 
				
			||||||
 | 
					            author=User.objects.get(username="subscriber"),
 | 
				
			||||||
 | 
					            club=Club.objects.first(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cls.new = new
 | 
				
			||||||
 | 
					        cls.author = new.author
 | 
				
			||||||
 | 
					        cls.sli = User.objects.get(username="sli")
 | 
				
			||||||
 | 
					        cls.anonymous = AnonymousUser()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_news_owner(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that news are owned by com admins
 | 
				
			||||||
 | 
					        or by their author but nobody else
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(self.new.is_owned_by(self.com_admin))
 | 
				
			||||||
 | 
					        self.assertTrue(self.new.is_owned_by(self.author))
 | 
				
			||||||
 | 
					        self.assertFalse(self.new.is_owned_by(self.anonymous))
 | 
				
			||||||
 | 
					        self.assertFalse(self.new.is_owned_by(self.sli))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WeekmailArticleTest(TestCase):
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        cls.com_admin = User.objects.get(username="comunity")
 | 
				
			||||||
 | 
					        author = User.objects.get(username="subscriber")
 | 
				
			||||||
 | 
					        cls.article = WeekmailArticle.objects.create(
 | 
				
			||||||
 | 
					            weekmail=Weekmail.objects.create(),
 | 
				
			||||||
 | 
					            author=author,
 | 
				
			||||||
 | 
					            title="title",
 | 
				
			||||||
 | 
					            content="Some content",
 | 
				
			||||||
 | 
					            club=Club.objects.first(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cls.author = author
 | 
				
			||||||
 | 
					        cls.sli = User.objects.get(username="sli")
 | 
				
			||||||
 | 
					        cls.anonymous = AnonymousUser()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_weekmail_owner(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that weekmails are owned only by com admins
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.assertTrue(self.article.is_owned_by(self.com_admin))
 | 
				
			||||||
 | 
					        self.assertFalse(self.article.is_owned_by(self.author))
 | 
				
			||||||
 | 
					        self.assertFalse(self.article.is_owned_by(self.anonymous))
 | 
				
			||||||
 | 
					        self.assertFalse(self.article.is_owned_by(self.sli))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PosterTest(TestCase):
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        cls.com_admin = User.objects.get(username="comunity")
 | 
				
			||||||
 | 
					        cls.poster = Poster.objects.create(
 | 
				
			||||||
 | 
					            name="dummy",
 | 
				
			||||||
 | 
					            file=SimpleUploadedFile("dummy.jpg", b"azertyuiop"),
 | 
				
			||||||
 | 
					            club=Club.objects.first(),
 | 
				
			||||||
 | 
					            date_begin=localtime(now()),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cls.sli = User.objects.get(username="sli")
 | 
				
			||||||
 | 
					        cls.sli.memberships.all().delete()
 | 
				
			||||||
 | 
					        Membership(user=cls.sli, club=Club.objects.first(), role=5).save()
 | 
				
			||||||
 | 
					        cls.susbcriber = User.objects.get(username="subscriber")
 | 
				
			||||||
 | 
					        cls.anonymous = AnonymousUser()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_poster_owner(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test that poster are owned by com admins and board members in clubs
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.assertTrue(self.poster.is_owned_by(self.com_admin))
 | 
				
			||||||
 | 
					        self.assertFalse(self.poster.is_owned_by(self.anonymous))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertFalse(self.poster.is_owned_by(self.susbcriber))
 | 
				
			||||||
 | 
					        self.assertTrue(self.poster.is_owned_by(self.sli))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										122
									
								
								com/urls.py
									
									
									
									
									
								
							
							
						
						@@ -1,125 +1,111 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.urls import re_path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from com.views import *
 | 
					 | 
				
			||||||
from club.views import MailingDeleteView
 | 
					from club.views import MailingDeleteView
 | 
				
			||||||
 | 
					from com.views import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    re_path(r"^sith/edit/alert$", AlertMsgEditView.as_view(), name="alert_edit"),
 | 
					    path("sith/edit/alert/", AlertMsgEditView.as_view(), name="alert_edit"),
 | 
				
			||||||
    re_path(r"^sith/edit/info$", InfoMsgEditView.as_view(), name="info_edit"),
 | 
					    path("sith/edit/info/", InfoMsgEditView.as_view(), name="info_edit"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^sith/edit/weekmail_destinations$",
 | 
					        "sith/edit/weekmail_destinations/",
 | 
				
			||||||
        WeekmailDestinationEditView.as_view(),
 | 
					        WeekmailDestinationEditView.as_view(),
 | 
				
			||||||
        name="weekmail_destinations",
 | 
					        name="weekmail_destinations",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(r"^weekmail$", WeekmailEditView.as_view(), name="weekmail"),
 | 
					    path("weekmail/", WeekmailEditView.as_view(), name="weekmail"),
 | 
				
			||||||
    re_path(
 | 
					    path("weekmail/preview/", WeekmailPreviewView.as_view(), name="weekmail_preview"),
 | 
				
			||||||
        r"^weekmail/preview$", WeekmailPreviewView.as_view(), name="weekmail_preview"
 | 
					    path(
 | 
				
			||||||
    ),
 | 
					        "weekmail/new_article/",
 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^weekmail/new_article$",
 | 
					 | 
				
			||||||
        WeekmailArticleCreateView.as_view(),
 | 
					        WeekmailArticleCreateView.as_view(),
 | 
				
			||||||
        name="weekmail_article",
 | 
					        name="weekmail_article",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^weekmail/article/(?P<article_id>[0-9]+)/delete$",
 | 
					        "weekmail/article/<int:article_id>/delete/",
 | 
				
			||||||
        WeekmailArticleDeleteView.as_view(),
 | 
					        WeekmailArticleDeleteView.as_view(),
 | 
				
			||||||
        name="weekmail_article_delete",
 | 
					        name="weekmail_article_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^weekmail/article/(?P<article_id>[0-9]+)/edit$",
 | 
					        "weekmail/article/<int:article_id>/edit/",
 | 
				
			||||||
        WeekmailArticleEditView.as_view(),
 | 
					        WeekmailArticleEditView.as_view(),
 | 
				
			||||||
        name="weekmail_article_edit",
 | 
					        name="weekmail_article_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(r"^news$", NewsListView.as_view(), name="news_list"),
 | 
					    path("news/", NewsListView.as_view(), name="news_list"),
 | 
				
			||||||
    re_path(r"^news/admin$", NewsAdminListView.as_view(), name="news_admin_list"),
 | 
					    path("news/admin/", NewsAdminListView.as_view(), name="news_admin_list"),
 | 
				
			||||||
    re_path(r"^news/create$", NewsCreateView.as_view(), name="news_new"),
 | 
					    path("news/create/", NewsCreateView.as_view(), name="news_new"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^news/(?P<news_id>[0-9]+)/delete$",
 | 
					        "news/<int:news_id>/delete/",
 | 
				
			||||||
        NewsDeleteView.as_view(),
 | 
					        NewsDeleteView.as_view(),
 | 
				
			||||||
        name="news_delete",
 | 
					        name="news_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^news/(?P<news_id>[0-9]+)/moderate$",
 | 
					        "news/<int:news_id>/moderate/",
 | 
				
			||||||
        NewsModerateView.as_view(),
 | 
					        NewsModerateView.as_view(),
 | 
				
			||||||
        name="news_moderate",
 | 
					        name="news_moderate",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path("news/<int:news_id>/edit/", NewsEditView.as_view(), name="news_edit"),
 | 
				
			||||||
        r"^news/(?P<news_id>[0-9]+)/edit$", NewsEditView.as_view(), name="news_edit"
 | 
					    path("news/<int:news_id>/", NewsDetailView.as_view(), name="news_detail"),
 | 
				
			||||||
    ),
 | 
					    path("mailings/", MailingListAdminView.as_view(), name="mailing_admin"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^news/(?P<news_id>[0-9]+)$", NewsDetailView.as_view(), name="news_detail"
 | 
					        "mailings/<int:mailing_id>/moderate/",
 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    re_path(r"^mailings$", MailingListAdminView.as_view(), name="mailing_admin"),
 | 
					 | 
				
			||||||
    re_path(
 | 
					 | 
				
			||||||
        r"^mailings/(?P<mailing_id>[0-9]+)/moderate$",
 | 
					 | 
				
			||||||
        MailingModerateView.as_view(),
 | 
					        MailingModerateView.as_view(),
 | 
				
			||||||
        name="mailing_moderate",
 | 
					        name="mailing_moderate",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^mailings/(?P<mailing_id>[0-9]+)/delete$",
 | 
					        "mailings/<int:mailing_id>/delete/",
 | 
				
			||||||
        MailingDeleteView.as_view(redirect_page="com:mailing_admin"),
 | 
					        MailingDeleteView.as_view(redirect_page="com:mailing_admin"),
 | 
				
			||||||
        name="mailing_delete",
 | 
					        name="mailing_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(r"^poster$", PosterListView.as_view(), name="poster_list"),
 | 
					    path("poster/", PosterListView.as_view(), name="poster_list"),
 | 
				
			||||||
    re_path(r"^poster/create$", PosterCreateView.as_view(), name="poster_create"),
 | 
					    path("poster/create/", PosterCreateView.as_view(), name="poster_create"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^poster/(?P<poster_id>[0-9]+)/edit$",
 | 
					        "poster/<int:poster_id>/edit/",
 | 
				
			||||||
        PosterEditView.as_view(),
 | 
					        PosterEditView.as_view(),
 | 
				
			||||||
        name="poster_edit",
 | 
					        name="poster_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^poster/(?P<poster_id>[0-9]+)/delete$",
 | 
					        "poster/<int:poster_id>/delete/",
 | 
				
			||||||
        PosterDeleteView.as_view(),
 | 
					        PosterDeleteView.as_view(),
 | 
				
			||||||
        name="poster_delete",
 | 
					        name="poster_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^poster/moderate$",
 | 
					        "poster/moderate/",
 | 
				
			||||||
        PosterModerateListView.as_view(),
 | 
					        PosterModerateListView.as_view(),
 | 
				
			||||||
        name="poster_moderate_list",
 | 
					        name="poster_moderate_list",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^poster/(?P<object_id>[0-9]+)/moderate$",
 | 
					        "poster/<int:object_id>/moderate/",
 | 
				
			||||||
        PosterModerateView.as_view(),
 | 
					        PosterModerateView.as_view(),
 | 
				
			||||||
        name="poster_moderate",
 | 
					        name="poster_moderate",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(r"^screen$", ScreenListView.as_view(), name="screen_list"),
 | 
					    path("screen/", ScreenListView.as_view(), name="screen_list"),
 | 
				
			||||||
    re_path(r"^screen/create$", ScreenCreateView.as_view(), name="screen_create"),
 | 
					    path("screen/create/", ScreenCreateView.as_view(), name="screen_create"),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^screen/(?P<screen_id>[0-9]+)/slideshow$",
 | 
					        "screen/<int:screen_id>/slideshow/",
 | 
				
			||||||
        ScreenSlideshowView.as_view(),
 | 
					        ScreenSlideshowView.as_view(),
 | 
				
			||||||
        name="screen_slideshow",
 | 
					        name="screen_slideshow",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^screen/(?P<screen_id>[0-9]+)/edit$",
 | 
					        "screen/<int:screen_id>/edit/",
 | 
				
			||||||
        ScreenEditView.as_view(),
 | 
					        ScreenEditView.as_view(),
 | 
				
			||||||
        name="screen_edit",
 | 
					        name="screen_edit",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    re_path(
 | 
					    path(
 | 
				
			||||||
        r"^screen/(?P<screen_id>[0-9]+)/delete$",
 | 
					        "screen/<int:screen_id>/delete/",
 | 
				
			||||||
        ScreenDeleteView.as_view(),
 | 
					        ScreenDeleteView.as_view(),
 | 
				
			||||||
        name="screen_delete",
 | 
					        name="screen_delete",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								com/views.py
									
									
									
									
									
								
							
							
						
						@@ -146,7 +146,7 @@ class ComTabsMixin(TabedViewMixin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class IsComAdminMixin(View):
 | 
					class IsComAdminMixin(View):
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
        if not (request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)):
 | 
					        if not request.user.is_com_admin:
 | 
				
			||||||
            raise PermissionDenied
 | 
					            raise PermissionDenied
 | 
				
			||||||
        return super(IsComAdminMixin, self).dispatch(request, *args, **kwargs)
 | 
					        return super(IsComAdminMixin, self).dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -283,9 +283,7 @@ class NewsEditView(CanEditMixin, UpdateView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def form_valid(self, form):
 | 
					    def form_valid(self, form):
 | 
				
			||||||
        self.object = form.save()
 | 
					        self.object = form.save()
 | 
				
			||||||
        if form.cleaned_data["automoderation"] and self.request.user.is_in_group(
 | 
					        if form.cleaned_data["automoderation"] and self.request.user.is_com_admin:
 | 
				
			||||||
            settings.SITH_GROUP_COM_ADMIN_ID
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            self.object.moderator = self.request.user
 | 
					            self.object.moderator = self.request.user
 | 
				
			||||||
            self.object.is_moderated = True
 | 
					            self.object.is_moderated = True
 | 
				
			||||||
            self.object.save()
 | 
					            self.object.save()
 | 
				
			||||||
@@ -333,9 +331,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def form_valid(self, form):
 | 
					    def form_valid(self, form):
 | 
				
			||||||
        self.object = form.save()
 | 
					        self.object = form.save()
 | 
				
			||||||
        if form.cleaned_data["automoderation"] and self.request.user.is_in_group(
 | 
					        if form.cleaned_data["automoderation"] and self.request.user.is_com_admin:
 | 
				
			||||||
            settings.SITH_GROUP_COM_ADMIN_ID
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            self.object.moderator = self.request.user
 | 
					            self.object.moderator = self.request.user
 | 
				
			||||||
            self.object.is_moderated = True
 | 
					            self.object.is_moderated = True
 | 
				
			||||||
            self.object.save()
 | 
					            self.object.save()
 | 
				
			||||||
@@ -617,10 +613,7 @@ class MailingListAdminView(ComTabsMixin, ListView):
 | 
				
			|||||||
    current_tab = "mailings"
 | 
					    current_tab = "mailings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dispatch(self, request, *args, **kwargs):
 | 
					    def dispatch(self, request, *args, **kwargs):
 | 
				
			||||||
        if not (
 | 
					        if not (request.user.is_com_admin or request.user.is_root):
 | 
				
			||||||
            request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
 | 
					 | 
				
			||||||
            or request.user.is_root
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            raise PermissionDenied
 | 
					            raise PermissionDenied
 | 
				
			||||||
        return super(MailingListAdminView, self).dispatch(request, *args, **kwargs)
 | 
					        return super(MailingListAdminView, self).dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,40 +1,34 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from ajax_select import make_ajax_form
 | 
					from ajax_select import make_ajax_form
 | 
				
			||||||
from core.models import User, Page, RealGroup, SithFile
 | 
					from core.models import User, Page, RealGroup, MetaGroup, SithFile
 | 
				
			||||||
from django.contrib.auth.models import Group as AuthGroup
 | 
					from django.contrib.auth.models import Group as AuthGroup
 | 
				
			||||||
from haystack.admin import SearchModelAdmin
 | 
					from haystack.admin import SearchModelAdmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
admin.site.unregister(AuthGroup)
 | 
					admin.site.unregister(AuthGroup)
 | 
				
			||||||
 | 
					admin.site.register(MetaGroup)
 | 
				
			||||||
admin.site.register(RealGroup)
 | 
					admin.site.register(RealGroup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(User)
 | 
				
			||||||
class UserAdmin(SearchModelAdmin):
 | 
					class UserAdmin(SearchModelAdmin):
 | 
				
			||||||
    list_display = ["first_name", "last_name", "username", "email", "nick_name"]
 | 
					    list_display = ("first_name", "last_name", "username", "email", "nick_name")
 | 
				
			||||||
    form = make_ajax_form(
 | 
					    form = make_ajax_form(
 | 
				
			||||||
        User,
 | 
					        User,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -48,11 +42,9 @@ class UserAdmin(SearchModelAdmin):
 | 
				
			|||||||
    search_fields = ["first_name", "last_name", "username"]
 | 
					    search_fields = ["first_name", "last_name", "username"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
admin.site.register(User, UserAdmin)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@admin.register(Page)
 | 
					@admin.register(Page)
 | 
				
			||||||
class PageAdmin(admin.ModelAdmin):
 | 
					class PageAdmin(admin.ModelAdmin):
 | 
				
			||||||
 | 
					    list_display = ("name", "_full_name", "owner_group")
 | 
				
			||||||
    form = make_ajax_form(
 | 
					    form = make_ajax_form(
 | 
				
			||||||
        Page,
 | 
					        Page,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -66,4 +58,12 @@ class PageAdmin(admin.ModelAdmin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@admin.register(SithFile)
 | 
					@admin.register(SithFile)
 | 
				
			||||||
class SithFileAdmin(admin.ModelAdmin):
 | 
					class SithFileAdmin(admin.ModelAdmin):
 | 
				
			||||||
    form = make_ajax_form(SithFile, {"parent": "files"})  # ManyToManyField
 | 
					    list_display = ("name", "owner", "size", "date", "is_in_sas")
 | 
				
			||||||
 | 
					    form = make_ajax_form(
 | 
				
			||||||
 | 
					        SithFile,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "parent": "files",
 | 
				
			||||||
 | 
					            "owner": "users",
 | 
				
			||||||
 | 
					            "moderator": "users",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )  # ManyToManyField
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								core/apps.py
									
									
									
									
									
								
							
							
						
						@@ -25,6 +25,7 @@
 | 
				
			|||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.core.signals import request_started
 | 
					from django.core.signals import request_started
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,26 +34,17 @@ class SithConfig(AppConfig):
 | 
				
			|||||||
    verbose_name = "Core app of the Sith"
 | 
					    verbose_name = "Core app of the Sith"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ready(self):
 | 
					    def ready(self):
 | 
				
			||||||
        from core.models import User
 | 
					 | 
				
			||||||
        from club.models import Club
 | 
					 | 
				
			||||||
        from forum.models import Forum
 | 
					        from forum.models import Forum
 | 
				
			||||||
 | 
					        import core.signals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def clear_cached_groups(**kwargs):
 | 
					        cache.clear()
 | 
				
			||||||
            User._group_ids = {}
 | 
					 | 
				
			||||||
            User._group_name = {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def clear_cached_memberships(**kwargs):
 | 
					        def clear_cached_memberships(**kwargs):
 | 
				
			||||||
            User._club_memberships = {}
 | 
					 | 
				
			||||||
            Club._memberships = {}
 | 
					 | 
				
			||||||
            Forum._club_memberships = {}
 | 
					            Forum._club_memberships = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print("Connecting signals!", file=sys.stderr)
 | 
					        print("Connecting signals!", file=sys.stderr)
 | 
				
			||||||
        request_started.connect(
 | 
					 | 
				
			||||||
            clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups"
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        request_started.connect(
 | 
					        request_started.connect(
 | 
				
			||||||
            clear_cached_memberships,
 | 
					            clear_cached_memberships,
 | 
				
			||||||
            weak=False,
 | 
					            weak=False,
 | 
				
			||||||
            dispatch_uid="clear_cached_memberships",
 | 
					            dispatch_uid="clear_cached_memberships",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        # TODO: there may be a need to add more cache clearing
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								core/converters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					from core.models import Page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FourDigitYearConverter:
 | 
				
			||||||
 | 
					    regex = "[0-9]{4}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value):
 | 
				
			||||||
 | 
					        return int(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_url(self, value):
 | 
				
			||||||
 | 
					        return str(value).zfill(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TwoDigitMonthConverter:
 | 
				
			||||||
 | 
					    regex = "[0-9]{2}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value):
 | 
				
			||||||
 | 
					        return int(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_url(self, value):
 | 
				
			||||||
 | 
					        return str(value).zfill(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BooleanStringConverter:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Converter whose regex match either True or False
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    regex = r"(True)|(False)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value):
 | 
				
			||||||
 | 
					        return str(value) == "True"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_url(self, value):
 | 
				
			||||||
 | 
					        return str(value)
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB  | 
| 
		 Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB  | 
| 
		 Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								core/fixtures/images/sas/Family/skia_sli.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 57 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								core/fixtures/images/sas/Family/skia_sli_krophil.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 96 KiB  | 
| 
		 Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB  | 
| 
		 After Width: | Height: | Size: 58 KiB  | 
| 
		 After Width: | Height: | Size: 27 KiB  | 
| 
		 After Width: | Height: | Size: 24 KiB  | 
| 
		 After Width: | Height: | Size: 30 KiB  | 
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,8 +20,9 @@ from ajax_select import register, LookupChannel
 | 
				
			|||||||
from core.views.site import search_user
 | 
					from core.views.site import search_user
 | 
				
			||||||
from core.models import User, Group, SithFile
 | 
					from core.models import User, Group, SithFile
 | 
				
			||||||
from club.models import Club
 | 
					from club.models import Club
 | 
				
			||||||
from counter.models import Product, Counter
 | 
					from counter.models import Product, Counter, Customer
 | 
				
			||||||
from accounting.models import ClubAccount, Company
 | 
					from accounting.models import ClubAccount, Company
 | 
				
			||||||
 | 
					from eboutic.models import BasketItem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_token(request):
 | 
					def check_token(request):
 | 
				
			||||||
@@ -60,6 +53,21 @@ class UsersLookup(RightManagedLookupChannel):
 | 
				
			|||||||
        return item.get_display_name()
 | 
					        return item.get_display_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register("customers")
 | 
				
			||||||
 | 
					class CustomerLookup(RightManagedLookupChannel):
 | 
				
			||||||
 | 
					    model = Customer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_query(self, q, request):
 | 
				
			||||||
 | 
					        users = search_user(q)
 | 
				
			||||||
 | 
					        return [user.customer for user in users]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_match(self, obj):
 | 
				
			||||||
 | 
					        return obj.user.get_mini_item()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_item_display(self, obj):
 | 
				
			||||||
 | 
					        return f"{obj.user.get_display_name()} ({obj.account_id})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register("groups")
 | 
					@register("groups")
 | 
				
			||||||
class GroupsLookup(RightManagedLookupChannel):
 | 
					class GroupsLookup(RightManagedLookupChannel):
 | 
				
			||||||
    model = Group
 | 
					    model = Group
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,15 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,15 @@ from django.core.management.base import BaseCommand
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
 | 
					# see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
 | 
				
			||||||
# added "v?"
 | 
					# added "v?"
 | 
				
			||||||
 | 
					# Please note that this does not match the version of the three.js library.
 | 
				
			||||||
 | 
					# Hence, you shall have to check this one by yourself
 | 
				
			||||||
semver_regex = re.compile(
 | 
					semver_regex = re.compile(
 | 
				
			||||||
    """^v?(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"""
 | 
					    r"^v?"
 | 
				
			||||||
 | 
					    r"(?P<major>\d+)"
 | 
				
			||||||
 | 
					    r"\.(?P<minor>\d+)"
 | 
				
			||||||
 | 
					    r"\.(?P<patch>\d+)"
 | 
				
			||||||
 | 
					    r"(?:-(?P<prerelease>(?:\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
 | 
				
			||||||
 | 
					    r"(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,5 @@ class Command(compilemessages.Command):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        os.chdir("sith")
 | 
					        os.chdir("sith")
 | 
				
			||||||
        super(Command, self).handle(*args, **options)
 | 
					        super(Command, self).handle(*args, **options)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ class Command(BaseCommand):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def compilescss(self, file):
 | 
					    def compilescss(self, file):
 | 
				
			||||||
        print("compiling %s" % file)
 | 
					        print("compiling %s" % file)
 | 
				
			||||||
        with (open(file.replace(".scss", ".css"), "w")) as newfile:
 | 
					        with open(file.replace(".scss", ".css"), "w") as newfile:
 | 
				
			||||||
            newfile.write(self.compile(file))
 | 
					            newfile.write(self.compile(file))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def removescss(self, file):
 | 
					    def removescss(self, file):
 | 
				
			||||||
@@ -68,7 +68,6 @@ class Command(BaseCommand):
 | 
				
			|||||||
        os.remove(file)
 | 
					        os.remove(file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
 | 
					 | 
				
			||||||
        if os.path.isdir(settings.STATIC_ROOT):
 | 
					        if os.path.isdir(settings.STATIC_ROOT):
 | 
				
			||||||
            print("---- Compiling scss files ---")
 | 
					            print("---- Compiling scss files ---")
 | 
				
			||||||
            self.exec_on_folder(settings.STATIC_ROOT, self.compilescss)
 | 
					            self.exec_on_folder(settings.STATIC_ROOT, self.compilescss)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2016,2017,2023
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# - Skia <skia@hya.sk>
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# http://ae.utbm.fr.
 | 
				
			||||||
@@ -25,7 +25,9 @@
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
from datetime import date, datetime, timedelta
 | 
					from datetime import date, datetime, timedelta
 | 
				
			||||||
from io import StringIO, BytesIO
 | 
					from io import StringIO, BytesIO
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.auth.models import Permission
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from django.core.management import call_command
 | 
					from django.core.management import call_command
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
@@ -53,6 +55,7 @@ from com.models import Sith, Weekmail, News, NewsDate
 | 
				
			|||||||
from election.models import Election, Role, Candidature, ElectionList
 | 
					from election.models import Election, Role, Candidature, ElectionList
 | 
				
			||||||
from forum.models import Forum, ForumTopic
 | 
					from forum.models import Forum, ForumTopic
 | 
				
			||||||
from pedagogy.models import UV
 | 
					from pedagogy.models import UV
 | 
				
			||||||
 | 
					from sas.models import Album, Picture, PeoplePictureRelation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
@@ -70,10 +73,8 @@ class Command(BaseCommand):
 | 
				
			|||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        os.environ["DJANGO_COLORS"] = "nocolor"
 | 
					        os.environ["DJANGO_COLORS"] = "nocolor"
 | 
				
			||||||
        Site(id=4000, domain=settings.SITH_URL, name=settings.SITH_NAME).save()
 | 
					        Site(id=4000, domain=settings.SITH_URL, name=settings.SITH_NAME).save()
 | 
				
			||||||
        root_path = os.path.dirname(
 | 
					        root_path = Path(__file__).parent.parent.parent.parent
 | 
				
			||||||
            os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
 | 
					        root_group, _ = Group.objects.get_or_create(name="Root")
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        Group(name="Root").save()
 | 
					 | 
				
			||||||
        Group(name="Public").save()
 | 
					        Group(name="Public").save()
 | 
				
			||||||
        Group(name="Subscribers").save()
 | 
					        Group(name="Subscribers").save()
 | 
				
			||||||
        Group(name="Old subscribers").save()
 | 
					        Group(name="Old subscribers").save()
 | 
				
			||||||
@@ -83,10 +84,15 @@ class Command(BaseCommand):
 | 
				
			|||||||
        Group(name="Banned from buying alcohol").save()
 | 
					        Group(name="Banned from buying alcohol").save()
 | 
				
			||||||
        Group(name="Banned from counters").save()
 | 
					        Group(name="Banned from counters").save()
 | 
				
			||||||
        Group(name="Banned to subscribe").save()
 | 
					        Group(name="Banned to subscribe").save()
 | 
				
			||||||
        Group(name="SAS admin").save()
 | 
					        sas_admin, _ = Group.objects.get_or_create(name="SAS admin")
 | 
				
			||||||
        Group(name="Forum admin").save()
 | 
					        Group(name="Forum admin").save()
 | 
				
			||||||
        Group(name="Pedagogy admin").save()
 | 
					        Group(name="Pedagogy admin").save()
 | 
				
			||||||
        self.reset_index("core", "auth")
 | 
					        self.reset_index("core", "auth")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        change_billing = Permission.objects.get(codename="change_billinginfo")
 | 
				
			||||||
 | 
					        add_billing = Permission.objects.get(codename="add_billinginfo")
 | 
				
			||||||
 | 
					        root_group.permissions.add(change_billing, add_billing)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        root = User(
 | 
					        root = User(
 | 
				
			||||||
            id=0,
 | 
					            id=0,
 | 
				
			||||||
            username="root",
 | 
					            username="root",
 | 
				
			||||||
@@ -113,7 +119,8 @@ class Command(BaseCommand):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        club_root = SithFile(parent=None, name="clubs", is_folder=True, owner=root)
 | 
					        club_root = SithFile(parent=None, name="clubs", is_folder=True, owner=root)
 | 
				
			||||||
        club_root.save()
 | 
					        club_root.save()
 | 
				
			||||||
        SithFile(parent=None, name="SAS", is_folder=True, owner=root).save()
 | 
					        sas = SithFile(parent=None, name="SAS", is_folder=True, owner=root)
 | 
				
			||||||
 | 
					        sas.save()
 | 
				
			||||||
        main_club = Club(
 | 
					        main_club = Club(
 | 
				
			||||||
            id=1,
 | 
					            id=1,
 | 
				
			||||||
            name=settings.SITH_MAIN_CLUB["name"],
 | 
					            name=settings.SITH_MAIN_CLUB["name"],
 | 
				
			||||||
@@ -148,12 +155,10 @@ class Command(BaseCommand):
 | 
				
			|||||||
        Counter(name="Eboutic", club=main_club, type="EBOUTIC").save()
 | 
					        Counter(name="Eboutic", club=main_club, type="EBOUTIC").save()
 | 
				
			||||||
        Counter(name="AE", club=main_club, type="OFFICE").save()
 | 
					        Counter(name="AE", club=main_club, type="OFFICE").save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        home_root.view_groups.set(
 | 
					        ae_members = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
 | 
				
			||||||
            [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first()]
 | 
					
 | 
				
			||||||
        )
 | 
					        home_root.view_groups.set([ae_members])
 | 
				
			||||||
        club_root.view_groups.set(
 | 
					        club_root.view_groups.set([ae_members])
 | 
				
			||||||
            [Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first()]
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        home_root.save()
 | 
					        home_root.save()
 | 
				
			||||||
        club_root.save()
 | 
					        club_root.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -203,6 +208,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment
 | 
					        # Here we add a lot of test datas, that are not necessary for the Sith, but that provide a basic development environment
 | 
				
			||||||
        if not options["prod"]:
 | 
					        if not options["prod"]:
 | 
				
			||||||
 | 
					            self.now = timezone.now().replace(hour=12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Adding user Skia
 | 
					            # Adding user Skia
 | 
				
			||||||
            skia = User(
 | 
					            skia = User(
 | 
				
			||||||
                username="skia",
 | 
					                username="skia",
 | 
				
			||||||
@@ -213,11 +220,17 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            skia.set_password("plop")
 | 
					            skia.set_password("plop")
 | 
				
			||||||
            skia.save()
 | 
					            skia.save()
 | 
				
			||||||
            skia.view_groups = [
 | 
					            skia.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            skia.save()
 | 
					            skia.save()
 | 
				
			||||||
            skia_profile_path = os.path.join(root_path, "core/fixtures/images/3.jpg")
 | 
					            skia_profile_path = (
 | 
				
			||||||
 | 
					                root_path
 | 
				
			||||||
 | 
					                / "core"
 | 
				
			||||||
 | 
					                / "fixtures"
 | 
				
			||||||
 | 
					                / "images"
 | 
				
			||||||
 | 
					                / "sas"
 | 
				
			||||||
 | 
					                / "Family"
 | 
				
			||||||
 | 
					                / "skia.jpg"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            with open(skia_profile_path, "rb") as f:
 | 
					            with open(skia_profile_path, "rb") as f:
 | 
				
			||||||
                name = str(skia.id) + "_profile.jpg"
 | 
					                name = str(skia.id) + "_profile.jpg"
 | 
				
			||||||
                skia_profile = SithFile(
 | 
					                skia_profile = SithFile(
 | 
				
			||||||
@@ -227,7 +240,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
                    owner=skia,
 | 
					                    owner=skia,
 | 
				
			||||||
                    is_folder=False,
 | 
					                    is_folder=False,
 | 
				
			||||||
                    mime_type="image/jpeg",
 | 
					                    mime_type="image/jpeg",
 | 
				
			||||||
                    size=os.path.getsize(skia_profile_path),
 | 
					                    size=skia_profile_path.stat().st_size,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                skia_profile.file.name = name
 | 
					                skia_profile.file.name = name
 | 
				
			||||||
                skia_profile.save()
 | 
					                skia_profile.save()
 | 
				
			||||||
@@ -246,9 +259,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            public.set_password("plop")
 | 
					            public.set_password("plop")
 | 
				
			||||||
            public.save()
 | 
					            public.save()
 | 
				
			||||||
            public.view_groups = [
 | 
					            public.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            public.save()
 | 
					            public.save()
 | 
				
			||||||
            # Adding user Subscriber
 | 
					            # Adding user Subscriber
 | 
				
			||||||
            subscriber = User(
 | 
					            subscriber = User(
 | 
				
			||||||
@@ -262,9 +273,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            subscriber.set_password("plop")
 | 
					            subscriber.set_password("plop")
 | 
				
			||||||
            subscriber.save()
 | 
					            subscriber.save()
 | 
				
			||||||
            subscriber.view_groups = [
 | 
					            subscriber.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            subscriber.save()
 | 
					            subscriber.save()
 | 
				
			||||||
            # Adding user old Subscriber
 | 
					            # Adding user old Subscriber
 | 
				
			||||||
            old_subscriber = User(
 | 
					            old_subscriber = User(
 | 
				
			||||||
@@ -278,9 +287,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            old_subscriber.set_password("plop")
 | 
					            old_subscriber.set_password("plop")
 | 
				
			||||||
            old_subscriber.save()
 | 
					            old_subscriber.save()
 | 
				
			||||||
            old_subscriber.view_groups = [
 | 
					            old_subscriber.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            old_subscriber.save()
 | 
					            old_subscriber.save()
 | 
				
			||||||
            # Adding user Counter admin
 | 
					            # Adding user Counter admin
 | 
				
			||||||
            counter = User(
 | 
					            counter = User(
 | 
				
			||||||
@@ -294,9 +301,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            counter.set_password("plop")
 | 
					            counter.set_password("plop")
 | 
				
			||||||
            counter.save()
 | 
					            counter.save()
 | 
				
			||||||
            counter.view_groups = [
 | 
					            counter.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            counter.groups.set(
 | 
					            counter.groups.set(
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
                    Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID)
 | 
					                    Group.objects.filter(id=settings.SITH_GROUP_COUNTER_ADMIN_ID)
 | 
				
			||||||
@@ -317,9 +322,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            comptable.set_password("plop")
 | 
					            comptable.set_password("plop")
 | 
				
			||||||
            comptable.save()
 | 
					            comptable.save()
 | 
				
			||||||
            comptable.view_groups = [
 | 
					            comptable.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            comptable.groups.set(
 | 
					            comptable.groups.set(
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
                    Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
 | 
					                    Group.objects.filter(id=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
 | 
				
			||||||
@@ -340,28 +343,49 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            u.set_password("plop")
 | 
					            u.set_password("plop")
 | 
				
			||||||
            u.save()
 | 
					            u.save()
 | 
				
			||||||
            u.view_groups = [
 | 
					            u.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            u.save()
 | 
					            u.save()
 | 
				
			||||||
            # Adding user Richard Batsbak
 | 
					            # Adding user Richard Batsbak
 | 
				
			||||||
            r = User(
 | 
					            richard = User(
 | 
				
			||||||
                username="rbatsbak",
 | 
					                username="rbatsbak",
 | 
				
			||||||
                last_name="Batsbak",
 | 
					                last_name="Batsbak",
 | 
				
			||||||
                first_name="Richard",
 | 
					                first_name="Richard",
 | 
				
			||||||
                email="richard@git.an",
 | 
					                email="richard@git.an",
 | 
				
			||||||
                date_of_birth="1982-06-12",
 | 
					                date_of_birth="1982-06-12",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            r.set_password("plop")
 | 
					            richard.set_password("plop")
 | 
				
			||||||
            r.save()
 | 
					            richard.save()
 | 
				
			||||||
            r.view_groups = [
 | 
					            richard.godfathers.add(comptable)
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					            richard_profile_path = (
 | 
				
			||||||
            ]
 | 
					                root_path
 | 
				
			||||||
            r.save()
 | 
					                / "core"
 | 
				
			||||||
 | 
					                / "fixtures"
 | 
				
			||||||
 | 
					                / "images"
 | 
				
			||||||
 | 
					                / "sas"
 | 
				
			||||||
 | 
					                / "Family"
 | 
				
			||||||
 | 
					                / "richard.jpg"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            with open(richard_profile_path, "rb") as f:
 | 
				
			||||||
 | 
					                name = f"{richard.id}_profile.jpg"
 | 
				
			||||||
 | 
					                richard_profile = SithFile(
 | 
				
			||||||
 | 
					                    parent=profiles_root,
 | 
				
			||||||
 | 
					                    name=name,
 | 
				
			||||||
 | 
					                    file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
 | 
				
			||||||
 | 
					                    owner=richard,
 | 
				
			||||||
 | 
					                    is_folder=False,
 | 
				
			||||||
 | 
					                    mime_type="image/jpeg",
 | 
				
			||||||
 | 
					                    size=richard_profile_path.stat().st_size,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                richard_profile.file.name = name
 | 
				
			||||||
 | 
					                richard_profile.save()
 | 
				
			||||||
 | 
					                richard.profile_pict = richard_profile
 | 
				
			||||||
 | 
					                richard.save()
 | 
				
			||||||
 | 
					            richard.view_groups = [ae_members.id]
 | 
				
			||||||
 | 
					            richard.save()
 | 
				
			||||||
            # Adding syntax help page
 | 
					            # Adding syntax help page
 | 
				
			||||||
            p = Page(name="Aide_sur_la_syntaxe")
 | 
					            p = Page(name="Aide_sur_la_syntaxe")
 | 
				
			||||||
            p.save(force_lock=True)
 | 
					            p.save(force_lock=True)
 | 
				
			||||||
            with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as rm:
 | 
					            with open(root_path / "doc" / "SYNTAX.md", "r") as rm:
 | 
				
			||||||
                PageRev(
 | 
					                PageRev(
 | 
				
			||||||
                    page=p, title="Aide sur la syntaxe", author=skia, content=rm.read()
 | 
					                    page=p, title="Aide sur la syntaxe", author=skia, content=rm.read()
 | 
				
			||||||
                ).save()
 | 
					                ).save()
 | 
				
			||||||
@@ -388,7 +412,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            default_subscription = "un-semestre"
 | 
					            default_subscription = "un-semestre"
 | 
				
			||||||
            # Root
 | 
					            # Root
 | 
				
			||||||
            s = Subscription(
 | 
					            s = Subscription(
 | 
				
			||||||
                member=User.objects.filter(pk=root.pk).first(),
 | 
					                member=root,
 | 
				
			||||||
                subscription_type=default_subscription,
 | 
					                subscription_type=default_subscription,
 | 
				
			||||||
                payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
 | 
					                payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@@ -436,7 +460,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            s.save()
 | 
					            s.save()
 | 
				
			||||||
            # Richard
 | 
					            # Richard
 | 
				
			||||||
            s = Subscription(
 | 
					            s = Subscription(
 | 
				
			||||||
                member=User.objects.filter(pk=r.pk).first(),
 | 
					                member=User.objects.filter(pk=richard.pk).first(),
 | 
				
			||||||
                subscription_type=default_subscription,
 | 
					                subscription_type=default_subscription,
 | 
				
			||||||
                payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
 | 
					                payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@@ -488,7 +512,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            Club(
 | 
					            Club(
 | 
				
			||||||
                name="Woenzel'UT", unix_name="woenzel", address="Woenzel", parent=guyut
 | 
					                name="Woenzel'UT", unix_name="woenzel", address="Woenzel", parent=guyut
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            Membership(user=skia, club=main_club, role=3, description="").save()
 | 
					            Membership(user=skia, club=main_club, role=3).save()
 | 
				
			||||||
            troll = Club(
 | 
					            troll = Club(
 | 
				
			||||||
                name="Troll Penché",
 | 
					                name="Troll Penché",
 | 
				
			||||||
                unix_name="troll",
 | 
					                unix_name="troll",
 | 
				
			||||||
@@ -505,8 +529,10 @@ Welcome to the wiki page!
 | 
				
			|||||||
            refound.save()
 | 
					            refound.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Counters
 | 
					            # Counters
 | 
				
			||||||
 | 
					            subscribers = Group.objects.get(name="Subscribers")
 | 
				
			||||||
 | 
					            old_subscribers = Group.objects.get(name="Old subscribers")
 | 
				
			||||||
            Customer(user=skia, account_id="6568j", amount=0).save()
 | 
					            Customer(user=skia, account_id="6568j", amount=0).save()
 | 
				
			||||||
            Customer(user=r, account_id="4000k", amount=0).save()
 | 
					            Customer(user=richard, account_id="4000k", amount=0).save()
 | 
				
			||||||
            p = ProductType(name="Bières bouteilles")
 | 
					            p = ProductType(name="Bières bouteilles")
 | 
				
			||||||
            p.save()
 | 
					            p.save()
 | 
				
			||||||
            c = ProductType(name="Cotisations")
 | 
					            c = ProductType(name="Cotisations")
 | 
				
			||||||
@@ -525,6 +551,9 @@ Welcome to the wiki page!
 | 
				
			|||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            cotis.save()
 | 
					            cotis.save()
 | 
				
			||||||
 | 
					            cotis.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            cotis.buying_groups.add(old_subscribers)
 | 
				
			||||||
 | 
					            cotis.save()
 | 
				
			||||||
            cotis2 = Product(
 | 
					            cotis2 = Product(
 | 
				
			||||||
                name="Cotis 2 semestres",
 | 
					                name="Cotis 2 semestres",
 | 
				
			||||||
                code="2SCOTIZ",
 | 
					                code="2SCOTIZ",
 | 
				
			||||||
@@ -535,6 +564,9 @@ Welcome to the wiki page!
 | 
				
			|||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            cotis2.save()
 | 
					            cotis2.save()
 | 
				
			||||||
 | 
					            cotis2.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            cotis2.buying_groups.add(old_subscribers)
 | 
				
			||||||
 | 
					            cotis2.save()
 | 
				
			||||||
            refill = Product(
 | 
					            refill = Product(
 | 
				
			||||||
                name="Rechargement 15 €",
 | 
					                name="Rechargement 15 €",
 | 
				
			||||||
                code="15REFILL",
 | 
					                code="15REFILL",
 | 
				
			||||||
@@ -545,6 +577,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            refill.save()
 | 
					            refill.save()
 | 
				
			||||||
 | 
					            refill.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            refill.save()
 | 
				
			||||||
            barb = Product(
 | 
					            barb = Product(
 | 
				
			||||||
                name="Barbar",
 | 
					                name="Barbar",
 | 
				
			||||||
                code="BARB",
 | 
					                code="BARB",
 | 
				
			||||||
@@ -553,8 +587,11 @@ Welcome to the wiki page!
 | 
				
			|||||||
                selling_price="1.7",
 | 
					                selling_price="1.7",
 | 
				
			||||||
                special_selling_price="1.6",
 | 
					                special_selling_price="1.6",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
 | 
					                limit_age=18,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            barb.save()
 | 
					            barb.save()
 | 
				
			||||||
 | 
					            barb.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            barb.save()
 | 
				
			||||||
            cble = Product(
 | 
					            cble = Product(
 | 
				
			||||||
                name="Chimay Bleue",
 | 
					                name="Chimay Bleue",
 | 
				
			||||||
                code="CBLE",
 | 
					                code="CBLE",
 | 
				
			||||||
@@ -563,8 +600,11 @@ Welcome to the wiki page!
 | 
				
			|||||||
                selling_price="1.7",
 | 
					                selling_price="1.7",
 | 
				
			||||||
                special_selling_price="1.6",
 | 
					                special_selling_price="1.6",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
 | 
					                limit_age=18,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            cble.save()
 | 
					            cble.save()
 | 
				
			||||||
 | 
					            cble.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            cble.save()
 | 
				
			||||||
            cons = Product(
 | 
					            cons = Product(
 | 
				
			||||||
                name="Consigne Eco-cup",
 | 
					                name="Consigne Eco-cup",
 | 
				
			||||||
                code="CONS",
 | 
					                code="CONS",
 | 
				
			||||||
@@ -574,7 +614,6 @@ Welcome to the wiki page!
 | 
				
			|||||||
                special_selling_price="1",
 | 
					                special_selling_price="1",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            cons.id = 1152
 | 
					 | 
				
			||||||
            cons.save()
 | 
					            cons.save()
 | 
				
			||||||
            dcons = Product(
 | 
					            dcons = Product(
 | 
				
			||||||
                name="Déconsigne Eco-cup",
 | 
					                name="Déconsigne Eco-cup",
 | 
				
			||||||
@@ -585,9 +624,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
                special_selling_price="-1",
 | 
					                special_selling_price="-1",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            dcons.id = 1151
 | 
					 | 
				
			||||||
            dcons.save()
 | 
					            dcons.save()
 | 
				
			||||||
            Product(
 | 
					            cors = Product(
 | 
				
			||||||
                name="Corsendonk",
 | 
					                name="Corsendonk",
 | 
				
			||||||
                code="CORS",
 | 
					                code="CORS",
 | 
				
			||||||
                product_type=p,
 | 
					                product_type=p,
 | 
				
			||||||
@@ -595,8 +633,12 @@ Welcome to the wiki page!
 | 
				
			|||||||
                selling_price="1.7",
 | 
					                selling_price="1.7",
 | 
				
			||||||
                special_selling_price="1.6",
 | 
					                special_selling_price="1.6",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            ).save()
 | 
					                limit_age=18,
 | 
				
			||||||
            Product(
 | 
					            )
 | 
				
			||||||
 | 
					            cors.save()
 | 
				
			||||||
 | 
					            cors.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            cors.save()
 | 
				
			||||||
 | 
					            carolus = Product(
 | 
				
			||||||
                name="Carolus",
 | 
					                name="Carolus",
 | 
				
			||||||
                code="CARO",
 | 
					                code="CARO",
 | 
				
			||||||
                product_type=p,
 | 
					                product_type=p,
 | 
				
			||||||
@@ -604,7 +646,11 @@ Welcome to the wiki page!
 | 
				
			|||||||
                selling_price="1.7",
 | 
					                selling_price="1.7",
 | 
				
			||||||
                special_selling_price="1.6",
 | 
					                special_selling_price="1.6",
 | 
				
			||||||
                club=main_club,
 | 
					                club=main_club,
 | 
				
			||||||
            ).save()
 | 
					                limit_age=18,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            carolus.save()
 | 
				
			||||||
 | 
					            carolus.buying_groups.add(subscribers)
 | 
				
			||||||
 | 
					            carolus.save()
 | 
				
			||||||
            mde = Counter.objects.filter(name="MDE").first()
 | 
					            mde = Counter.objects.filter(name="MDE").first()
 | 
				
			||||||
            mde.products.add(barb)
 | 
					            mde.products.add(barb)
 | 
				
			||||||
            mde.products.add(cble)
 | 
					            mde.products.add(cble)
 | 
				
			||||||
@@ -793,11 +839,17 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            sli.set_password("plop")
 | 
					            sli.set_password("plop")
 | 
				
			||||||
            sli.save()
 | 
					            sli.save()
 | 
				
			||||||
            sli.view_groups = [
 | 
					            sli.view_groups = [ae_members.id]
 | 
				
			||||||
                Group.objects.filter(name=settings.SITH_MAIN_MEMBERS_GROUP).first().id
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
            sli.save()
 | 
					            sli.save()
 | 
				
			||||||
            sli_profile_path = os.path.join(root_path, "core/fixtures/images/5.jpg")
 | 
					            sli_profile_path = (
 | 
				
			||||||
 | 
					                root_path
 | 
				
			||||||
 | 
					                / "core"
 | 
				
			||||||
 | 
					                / "fixtures"
 | 
				
			||||||
 | 
					                / "images"
 | 
				
			||||||
 | 
					                / "sas"
 | 
				
			||||||
 | 
					                / "Family"
 | 
				
			||||||
 | 
					                / "sli.jpg"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            with open(sli_profile_path, "rb") as f:
 | 
					            with open(sli_profile_path, "rb") as f:
 | 
				
			||||||
                name = str(sli.id) + "_profile.jpg"
 | 
					                name = str(sli.id) + "_profile.jpg"
 | 
				
			||||||
                sli_profile = SithFile(
 | 
					                sli_profile = SithFile(
 | 
				
			||||||
@@ -807,7 +859,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
                    owner=sli,
 | 
					                    owner=sli,
 | 
				
			||||||
                    is_folder=False,
 | 
					                    is_folder=False,
 | 
				
			||||||
                    mime_type="image/jpeg",
 | 
					                    mime_type="image/jpeg",
 | 
				
			||||||
                    size=os.path.getsize(sli_profile_path),
 | 
					                    size=sli_profile_path.stat().st_size,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                sli_profile.file.name = name
 | 
					                sli_profile.file.name = name
 | 
				
			||||||
                sli_profile.save()
 | 
					                sli_profile.save()
 | 
				
			||||||
@@ -823,7 +875,15 @@ Welcome to the wiki page!
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            krophil.set_password("plop")
 | 
					            krophil.set_password("plop")
 | 
				
			||||||
            krophil.save()
 | 
					            krophil.save()
 | 
				
			||||||
            krophil_profile_path = os.path.join(root_path, "core/fixtures/images/6.jpg")
 | 
					            krophil_profile_path = (
 | 
				
			||||||
 | 
					                root_path
 | 
				
			||||||
 | 
					                / "core"
 | 
				
			||||||
 | 
					                / "fixtures"
 | 
				
			||||||
 | 
					                / "images"
 | 
				
			||||||
 | 
					                / "sas"
 | 
				
			||||||
 | 
					                / "Family"
 | 
				
			||||||
 | 
					                / "krophil.jpg"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            with open(krophil_profile_path, "rb") as f:
 | 
					            with open(krophil_profile_path, "rb") as f:
 | 
				
			||||||
                name = str(krophil.id) + "_profile.jpg"
 | 
					                name = str(krophil.id) + "_profile.jpg"
 | 
				
			||||||
                krophil_profile = SithFile(
 | 
					                krophil_profile = SithFile(
 | 
				
			||||||
@@ -833,7 +893,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
                    owner=krophil,
 | 
					                    owner=krophil,
 | 
				
			||||||
                    is_folder=False,
 | 
					                    is_folder=False,
 | 
				
			||||||
                    mime_type="image/jpeg",
 | 
					                    mime_type="image/jpeg",
 | 
				
			||||||
                    size=os.path.getsize(krophil_profile_path),
 | 
					                    size=krophil_profile_path.stat().st_size,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                krophil_profile.file.name = name
 | 
					                krophil_profile.file.name = name
 | 
				
			||||||
                krophil_profile.save()
 | 
					                krophil_profile.save()
 | 
				
			||||||
@@ -856,7 +916,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            Membership(
 | 
					            Membership(
 | 
				
			||||||
                user=comunity,
 | 
					                user=comunity,
 | 
				
			||||||
                club=bar_club,
 | 
					                club=bar_club,
 | 
				
			||||||
                start_date=timezone.now(),
 | 
					                start_date=self.now,
 | 
				
			||||||
                role=settings.SITH_CLUB_ROLES_ID["Board member"],
 | 
					                role=settings.SITH_CLUB_ROLES_ID["Board member"],
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            # Adding user tutu
 | 
					            # Adding user tutu
 | 
				
			||||||
@@ -1015,7 +1075,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            ForumTopic(forum=hall)
 | 
					            ForumTopic(forum=hall)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # News
 | 
					            # News
 | 
				
			||||||
            friday = timezone.now()
 | 
					            friday = self.now
 | 
				
			||||||
            while friday.weekday() != 4:
 | 
					            while friday.weekday() != 4:
 | 
				
			||||||
                friday += timedelta(hours=6)
 | 
					                friday += timedelta(hours=6)
 | 
				
			||||||
            friday.replace(hour=20, minute=0, second=0)
 | 
					            friday.replace(hour=20, minute=0, second=0)
 | 
				
			||||||
@@ -1033,8 +1093,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
            n.save()
 | 
					            n.save()
 | 
				
			||||||
            NewsDate(
 | 
					            NewsDate(
 | 
				
			||||||
                news=n,
 | 
					                news=n,
 | 
				
			||||||
                start_date=timezone.now() + timedelta(hours=70),
 | 
					                start_date=self.now + timedelta(hours=70),
 | 
				
			||||||
                end_date=timezone.now() + timedelta(hours=72),
 | 
					                end_date=self.now + timedelta(hours=72),
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            n = News(
 | 
					            n = News(
 | 
				
			||||||
                title="Repas barman",
 | 
					                title="Repas barman",
 | 
				
			||||||
@@ -1050,8 +1110,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
            n.save()
 | 
					            n.save()
 | 
				
			||||||
            NewsDate(
 | 
					            NewsDate(
 | 
				
			||||||
                news=n,
 | 
					                news=n,
 | 
				
			||||||
                start_date=timezone.now() + timedelta(hours=72),
 | 
					                start_date=self.now + timedelta(hours=72),
 | 
				
			||||||
                end_date=timezone.now() + timedelta(hours=84),
 | 
					                end_date=self.now + timedelta(hours=84),
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            n = News(
 | 
					            n = News(
 | 
				
			||||||
                title="Repas fromager",
 | 
					                title="Repas fromager",
 | 
				
			||||||
@@ -1066,8 +1126,8 @@ Welcome to the wiki page!
 | 
				
			|||||||
            n.save()
 | 
					            n.save()
 | 
				
			||||||
            NewsDate(
 | 
					            NewsDate(
 | 
				
			||||||
                news=n,
 | 
					                news=n,
 | 
				
			||||||
                start_date=timezone.now() + timedelta(hours=96),
 | 
					                start_date=self.now + timedelta(hours=96),
 | 
				
			||||||
                end_date=timezone.now() + timedelta(hours=100),
 | 
					                end_date=self.now + timedelta(hours=100),
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            n = News(
 | 
					            n = News(
 | 
				
			||||||
                title="SdF",
 | 
					                title="SdF",
 | 
				
			||||||
@@ -1083,7 +1143,7 @@ Welcome to the wiki page!
 | 
				
			|||||||
            NewsDate(
 | 
					            NewsDate(
 | 
				
			||||||
                news=n,
 | 
					                news=n,
 | 
				
			||||||
                start_date=friday + timedelta(hours=24 * 7 + 1),
 | 
					                start_date=friday + timedelta(hours=24 * 7 + 1),
 | 
				
			||||||
                end_date=timezone.now() + timedelta(hours=24 * 7 + 9),
 | 
					                end_date=self.now + timedelta(hours=24 * 7 + 9),
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
            # Weekly
 | 
					            # Weekly
 | 
				
			||||||
            n = News(
 | 
					            n = News(
 | 
				
			||||||
@@ -1136,3 +1196,106 @@ Welcome to the wiki page!
 | 
				
			|||||||
                hours_THE=121,
 | 
					                hours_THE=121,
 | 
				
			||||||
                hours_TE=4,
 | 
					                hours_TE=4,
 | 
				
			||||||
            ).save()
 | 
					            ).save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # SAS
 | 
				
			||||||
 | 
					            skia.groups.add(sas_admin.id)
 | 
				
			||||||
 | 
					            sas_fixtures_path = root_path / "core" / "fixtures" / "images" / "sas"
 | 
				
			||||||
 | 
					            for f in sas_fixtures_path.glob("*"):
 | 
				
			||||||
 | 
					                if f.is_dir():
 | 
				
			||||||
 | 
					                    album = Album(
 | 
				
			||||||
 | 
					                        parent=sas,
 | 
				
			||||||
 | 
					                        name=f.name,
 | 
				
			||||||
 | 
					                        owner=root,
 | 
				
			||||||
 | 
					                        is_folder=True,
 | 
				
			||||||
 | 
					                        is_in_sas=True,
 | 
				
			||||||
 | 
					                        is_moderated=True,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    album.clean()
 | 
				
			||||||
 | 
					                    album.save()
 | 
				
			||||||
 | 
					                    for p in f.iterdir():
 | 
				
			||||||
 | 
					                        pict = Picture(
 | 
				
			||||||
 | 
					                            parent=album,
 | 
				
			||||||
 | 
					                            name=p.name,
 | 
				
			||||||
 | 
					                            file=resize_image(
 | 
				
			||||||
 | 
					                                Image.open(BytesIO(p.read_bytes())), 1000, "JPEG"
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                            owner=root,
 | 
				
			||||||
 | 
					                            is_folder=False,
 | 
				
			||||||
 | 
					                            is_in_sas=True,
 | 
				
			||||||
 | 
					                            is_moderated=True,
 | 
				
			||||||
 | 
					                            mime_type="image/jpeg",
 | 
				
			||||||
 | 
					                            size=p.stat().st_size,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        pict.file.name = p.name
 | 
				
			||||||
 | 
					                        pict.clean()
 | 
				
			||||||
 | 
					                        pict.generate_thumbnails()
 | 
				
			||||||
 | 
					                        pict.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="skia.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=skia, picture=p).save()
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="sli.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=sli, picture=p).save()
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="krophil.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=krophil, picture=p).save()
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="skia_sli.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=skia, picture=p).save()
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=sli, picture=p).save()
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="skia_sli_krophil.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=skia, picture=p).save()
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=sli, picture=p).save()
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=krophil, picture=p).save()
 | 
				
			||||||
 | 
					            p = Picture.objects.get(name="richard.jpg")
 | 
				
			||||||
 | 
					            PeoplePictureRelation(user=richard, picture=p).save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            with open(skia_profile_path, "rb") as f:
 | 
				
			||||||
 | 
					                name = str(skia.id) + "_profile.jpg"
 | 
				
			||||||
 | 
					                skia_profile = SithFile(
 | 
				
			||||||
 | 
					                    parent=profiles_root,
 | 
				
			||||||
 | 
					                    name=name,
 | 
				
			||||||
 | 
					                    file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
 | 
				
			||||||
 | 
					                    owner=skia,
 | 
				
			||||||
 | 
					                    is_folder=False,
 | 
				
			||||||
 | 
					                    mime_type="image/jpeg",
 | 
				
			||||||
 | 
					                    size=skia_profile_path.stat().st_size,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                skia_profile.file.name = name
 | 
				
			||||||
 | 
					                skia_profile.save()
 | 
				
			||||||
 | 
					                skia.profile_pict = skia_profile
 | 
				
			||||||
 | 
					                skia.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Create some additional data for galaxy to work with
 | 
				
			||||||
 | 
					            root.godfathers.add(skia)
 | 
				
			||||||
 | 
					            skia.godfathers.add(root)
 | 
				
			||||||
 | 
					            sli.godfathers.add(skia)
 | 
				
			||||||
 | 
					            richard.godchildren.add(subscriber)
 | 
				
			||||||
 | 
					            richard.godchildren.add(public)
 | 
				
			||||||
 | 
					            Membership(
 | 
				
			||||||
 | 
					                user=sli,
 | 
				
			||||||
 | 
					                club=troll,
 | 
				
			||||||
 | 
					                role=9,
 | 
				
			||||||
 | 
					                description="Padawan Troll",
 | 
				
			||||||
 | 
					                start_date=self.now - timedelta(days=17),
 | 
				
			||||||
 | 
					            ).save()
 | 
				
			||||||
 | 
					            Membership(
 | 
				
			||||||
 | 
					                user=krophil,
 | 
				
			||||||
 | 
					                club=troll,
 | 
				
			||||||
 | 
					                role=10,
 | 
				
			||||||
 | 
					                description="Maitre Troll",
 | 
				
			||||||
 | 
					                start_date=self.now - timedelta(days=200),
 | 
				
			||||||
 | 
					            ).save()
 | 
				
			||||||
 | 
					            Membership(
 | 
				
			||||||
 | 
					                user=skia,
 | 
				
			||||||
 | 
					                club=troll,
 | 
				
			||||||
 | 
					                role=2,
 | 
				
			||||||
 | 
					                description="Grand Ancien Troll",
 | 
				
			||||||
 | 
					                start_date=self.now - timedelta(days=400),
 | 
				
			||||||
 | 
					                end_date=self.now - timedelta(days=86),
 | 
				
			||||||
 | 
					            ).save()
 | 
				
			||||||
 | 
					            Membership(
 | 
				
			||||||
 | 
					                user=richard,
 | 
				
			||||||
 | 
					                club=troll,
 | 
				
			||||||
 | 
					                role=2,
 | 
				
			||||||
 | 
					                description="",
 | 
				
			||||||
 | 
					                start_date=self.now - timedelta(days=200),
 | 
				
			||||||
 | 
					                end_date=self.now - timedelta(days=100),
 | 
				
			||||||
 | 
					            ).save()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
# -*- coding:utf-8 -*
 | 
					# -*- coding:utf-8 -*
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Copyright 2016,2017
 | 
					# Copyright 2023 © AE UTBM
 | 
				
			||||||
# - Skia <skia@libskia.so>
 | 
					# ae@utbm.fr / ae.info@utbm.fr
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
 | 
					# This file is part of the website of the UTBM Student Association (AE UTBM),
 | 
				
			||||||
# http://ae.utbm.fr.
 | 
					# https://ae.utbm.fr.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software; you can redistribute it and/or modify it under
 | 
					# You can find the source code of the website at https://github.com/ae-utbm/sith3
 | 
				
			||||||
# the terms of the GNU General Public License a published by the Free Software
 | 
					 | 
				
			||||||
# Foundation; either version 3 of the License, or (at your option) any later
 | 
					 | 
				
			||||||
# version.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is distributed in the hope that it will be useful, but WITHOUT
 | 
					# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
 | 
				
			||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
					# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
 | 
				
			||||||
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
					# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
				
			||||||
# details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License along with
 | 
					 | 
				
			||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
 | 
					 | 
				
			||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,9 +22,6 @@ from django.core.management import call_command
 | 
				
			|||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
    help = "Set up a new instance of the Sith AE"
 | 
					    help = "Set up a new instance of the Sith AE"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_arguments(self, parser):
 | 
					 | 
				
			||||||
        parser.add_argument("--prod", action="store_true")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        root_path = os.path.dirname(
 | 
					        root_path = os.path.dirname(
 | 
				
			||||||
            os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
 | 
					            os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
 | 
				
			||||||
@@ -48,7 +37,4 @@ class Command(BaseCommand):
 | 
				
			|||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            repr(e)
 | 
					            repr(e)
 | 
				
			||||||
        call_command("migrate")
 | 
					        call_command("migrate")
 | 
				
			||||||
        if options["prod"]:
 | 
					        call_command("populate")
 | 
				
			||||||
            call_command("populate", "--prod")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            call_command("populate")
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||