mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
commit
eb04e26b22
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
|
||||
from club.models import Club, Membership
|
||||
@ -32,4 +31,4 @@ class MembershipAdmin(admin.ModelAdmin):
|
||||
"user__last_name",
|
||||
"club__name",
|
||||
)
|
||||
form = make_ajax_form(Membership, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
11
com/admin.py
11
com/admin.py
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
@ -23,19 +22,13 @@ from com.models import *
|
||||
class NewsAdmin(SearchModelAdmin):
|
||||
list_display = ("title", "type", "club", "author")
|
||||
search_fields = ("title", "summary", "content")
|
||||
form = make_ajax_form(
|
||||
News,
|
||||
{
|
||||
"author": "users",
|
||||
"moderator": "users",
|
||||
},
|
||||
)
|
||||
autocomplete_fields = ("author", "moderator")
|
||||
|
||||
|
||||
@admin.register(Poster)
|
||||
class PosterAdmin(SearchModelAdmin):
|
||||
list_display = ("name", "club", "date_begin", "date_end", "moderator")
|
||||
form = make_ajax_form(Poster, {"moderator": "users"})
|
||||
autocomplete_fields = ("moderator",)
|
||||
|
||||
|
||||
@admin.register(Weekmail)
|
||||
|
@ -13,30 +13,30 @@
|
||||
#
|
||||
#
|
||||
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group as AuthGroup
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
from core.models import MetaGroup, Page, RealGroup, SithFile, User
|
||||
from core.models import Group, Page, SithFile, User
|
||||
|
||||
admin.site.unregister(AuthGroup)
|
||||
admin.site.register(MetaGroup)
|
||||
admin.site.register(RealGroup)
|
||||
|
||||
|
||||
@admin.register(Group)
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "description", "is_meta")
|
||||
list_filter = ("is_meta",)
|
||||
search_fields = ("name",)
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class UserAdmin(SearchModelAdmin):
|
||||
class UserAdmin(admin.ModelAdmin):
|
||||
list_display = ("first_name", "last_name", "username", "email", "nick_name")
|
||||
form = make_ajax_form(
|
||||
User,
|
||||
{
|
||||
"godfathers": "users",
|
||||
"home": "files", # ManyToManyField
|
||||
"profile_pict": "files", # ManyToManyField
|
||||
"avatar_pict": "files", # ManyToManyField
|
||||
"scrub_pict": "files", # ManyToManyField
|
||||
},
|
||||
autocomplete_fields = (
|
||||
"godfathers",
|
||||
"home",
|
||||
"profile_pict",
|
||||
"avatar_pict",
|
||||
"scrub_pict",
|
||||
)
|
||||
search_fields = ["first_name", "last_name", "username"]
|
||||
|
||||
@ -44,25 +44,12 @@ class UserAdmin(SearchModelAdmin):
|
||||
@admin.register(Page)
|
||||
class PageAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "_full_name", "owner_group")
|
||||
form = make_ajax_form(
|
||||
Page,
|
||||
{
|
||||
"lock_user": "users",
|
||||
"owner_group": "groups",
|
||||
"edit_groups": "groups",
|
||||
"view_groups": "groups",
|
||||
},
|
||||
)
|
||||
search_fields = ("name",)
|
||||
autocomplete_fields = ("lock_user", "owner_group", "edit_groups", "view_groups")
|
||||
|
||||
|
||||
@admin.register(SithFile)
|
||||
class SithFileAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "owner", "size", "date", "is_in_sas")
|
||||
form = make_ajax_form(
|
||||
SithFile,
|
||||
{
|
||||
"parent": "files",
|
||||
"owner": "users",
|
||||
"moderator": "users",
|
||||
},
|
||||
) # ManyToManyField
|
||||
autocomplete_fields = ("parent", "owner", "moderator")
|
||||
search_fields = ("name", "parent__name")
|
||||
|
@ -27,4 +27,6 @@ $twitblue: hsl(206, 82%, 63%);
|
||||
|
||||
$shadow-color: rgb(223, 223, 223);
|
||||
|
||||
$background-button-color: hsl(0, 0%, 95%);
|
||||
$background-button-color: hsl(0, 0%, 95%);
|
||||
|
||||
$deepblue: #354a5f;
|
@ -1,7 +1,8 @@
|
||||
@import "colors";
|
||||
|
||||
$hovered-text-color: #c2c2c2;
|
||||
$text-color: white;
|
||||
|
||||
$background-color: #354a5f;
|
||||
$background-color-hovered: #283747;
|
||||
|
||||
$red-text-color: #eb2f06;
|
||||
@ -9,7 +10,7 @@ $hovered-red-text-color: #ff4d4d;
|
||||
|
||||
.header {
|
||||
box-sizing: border-box;
|
||||
background-color: $background-color;
|
||||
background-color: $deepblue;
|
||||
box-shadow: 3px 3px 3px 0 #dfdfdf;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
@ -98,7 +99,7 @@ $hovered-red-text-color: #ff4d4d;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
background-color: $background-color;
|
||||
background-color: $deepblue;
|
||||
width: 45px;
|
||||
height: 25px;
|
||||
padding: 0;
|
||||
@ -213,7 +214,7 @@ $hovered-red-text-color: #ff4d4d;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-color: $background-color;
|
||||
background-color: $deepblue;
|
||||
}
|
||||
|
||||
>.options {
|
||||
|
1
core/static/core/js/jszip/jszip-utils.min.js
vendored
1
core/static/core/js/jszip/jszip-utils.min.js
vendored
@ -1 +0,0 @@
|
||||
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){return function o(i,f,u){function s(n,e){if(!f[n]){if(!i[n]){var t="function"==typeof require&&require;if(!e&&t)return t(n,!0);if(a)return a(n,!0);throw new Error("Cannot find module '"+n+"'")}var r=f[n]={exports:{}};i[n][0].call(r.exports,function(e){var t=i[n][1][e];return s(t||e)},r,r.exports,o,i,f,u)}return f[n].exports}for(var a="function"==typeof require&&require,e=0;e<u.length;e++)s(u[e]);return s}({1:[function(e,t,n){"use strict";var u={};function r(){try{return new window.XMLHttpRequest}catch(e){}}u._getBinaryFromXHR=function(e){return e.response||e.responseText};var s="undefined"!=typeof window&&window.ActiveXObject?function(){return r()||function(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}()}:r;u.getBinaryContent=function(t,n){var e,r,o,i;"function"==typeof(n=n||{})?(i=n,n={}):"function"==typeof n.callback&&(i=n.callback),i||"undefined"==typeof Promise?(r=function(e){i(null,e)},o=function(e){i(e,null)}):e=new Promise(function(e,t){r=e,o=t});try{var f=s();f.open("GET",t,!0),"responseType"in f&&(f.responseType="arraybuffer"),f.overrideMimeType&&f.overrideMimeType("text/plain; charset=x-user-defined"),f.onreadystatechange=function(e){if(4===f.readyState)if(200===f.status||0===f.status)try{r(u._getBinaryFromXHR(f))}catch(e){o(new Error(e))}else o(new Error("Ajax error for "+t+" : "+this.status+" "+this.statusText))},n.progress&&(f.onprogress=function(e){n.progress({path:t,originalEvent:e,percent:e.loaded/e.total*100,loaded:e.loaded,total:e.total})}),f.send()}catch(e){o(new Error(e),null)}return e},t.exports=u},{}]},{},[1])(1)});
|
13
core/static/core/js/jszip/jszip.min.js
vendored
13
core/static/core/js/jszip/jszip.min.js
vendored
File diff suppressed because one or more lines are too long
28
core/static/core/js/zipjs/LICENSE
Normal file
28
core/static/core/js/zipjs/LICENSE
Normal file
@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2023, Gildas Lormeau
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
core/static/core/js/zipjs/README.md
Normal file
28
core/static/core/js/zipjs/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Built scripts of zip.js
|
||||
|
||||
**Warning**: These files are not compatible with ES modules, i.e. they cannot be imported with `import`. Instead, import `index.js` in the root folder of the project or one of the files prefixed with `zip-` in the [`/lib`](../lib) folder (e.g. `/lib/zip-no-worker-inflate.js`).
|
||||
|
||||
- for production (minified):
|
||||
|
||||
| | [`ZipReader`](https://gildas-lormeau.github.io/zip.js/api/classes/ZipReader.html) API | [`ZipWriter`](https://gildas-lormeau.github.io/zip.js/api/classes/ZipWriter.html) API | [`zip.fs`](https://gildas-lormeau.github.io/zip.js/api/classes/FS.html) API | Web Workers | No Web Workers | Usage |
|
||||
|--------------------------------|-----------------|-----------------|--------------|-------------|----------------|-------------------------------------------------------|
|
||||
| `zip.min.js` | x | x | | x | | compression/decompression with web workers |
|
||||
| `zip-no-worker.min.js` | x | x | | | x | compression/decompression without web workers |
|
||||
| `zip-no-worker-inflate.min.js` | x | | | | x | decompression without web workers |
|
||||
| `zip-no-worker-deflate.min.js` | | x | | | x | compression without web workers |
|
||||
| `zip-full.min.js` | x | x | | x | x | compression/decompression with or without web workers |
|
||||
| `zip-fs.min.js` | x | x | x | x | | compression/decompression with web workers |
|
||||
| `zip-fs-full.min.js` | x | x | x | x | x | compression/decompression with or without web workers |
|
||||
|
||||
- for development/debugging:
|
||||
|
||||
| | `zip` API | [`zip.fs`](https://gildas-lormeau.github.io/zip.js/api/classes/FS.html) API | Web Workers | No Web Workers |
|
||||
|-----------------------|-----------|--------------|-------------|----------------|
|
||||
| `zip.js` | x | | x | |
|
||||
| `zip-full.js` | x | | x | x |
|
||||
| `zip-fs.js` | x | x | x | |
|
||||
| `zip-fs-full.js` | x | x | x | x |
|
||||
|
||||
- `z-worker.js` can be used as a web worker script if the [Content Security Policy](https://developer.mozilla.org/docs/Web/HTTP/CSP) blocks scripts loaded with a `blob:` scheme
|
||||
- `z-worker-fflate.js` is the web worker script for using [fflate](https://gildas-lormeau.github.io/zip.js/core-api.html#alternative-codec-fflate)
|
||||
- `z-worker-pako.js` is the web worker script for using [pako](https://gildas-lormeau.github.io/zip.js/core-api.html#alternative-codec-pako)
|
1
core/static/core/js/zipjs/zip-fs-full.min.js
vendored
Normal file
1
core/static/core/js/zipjs/zip-fs-full.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -105,7 +105,7 @@ a:not(.button) {
|
||||
|
||||
.collapse-header {
|
||||
color: white;
|
||||
background-color: #354a5f;
|
||||
background-color: $deepblue;
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -206,34 +206,36 @@ a:not(.button) {
|
||||
width: 90%;
|
||||
margin: 20px auto 0;
|
||||
/*---------------------------------NAV---------------------------------*/
|
||||
|
||||
.btn {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
color: white;
|
||||
min-width: 60px;
|
||||
padding: 5px 10px;
|
||||
padding: 9px 13px;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
|
||||
&.btn-blue {
|
||||
background-color: #354a5f;
|
||||
}
|
||||
|
||||
&.btn-blue:disabled {
|
||||
background-color: rgba(70, 90, 126, 0.4);
|
||||
}
|
||||
|
||||
&.btn-blue.clickable:not(:disabled):hover {
|
||||
background-color: #2c3646;
|
||||
background-color: $deepblue;
|
||||
&:not(:disabled):hover {
|
||||
background-color: darken($deepblue, 10%);
|
||||
}
|
||||
&:disabled {
|
||||
background-color: rgba(70, 90, 126, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-grey {
|
||||
background-color: grey;
|
||||
&:not(:disabled):hover {
|
||||
background-color: darken(gray, 15%);
|
||||
}
|
||||
&:disabled {
|
||||
background-color: lighten(gray, 15%);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-grey.clickable:not(:disabled):hover {
|
||||
background-color: hsl(210, 5%, 30%);
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -977,7 +979,7 @@ thead td {
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: #354a5f;
|
||||
background-color: $deepblue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,12 @@
|
||||
{%- endblock -%}
|
||||
|
||||
{% block additional_js %}
|
||||
<script defer src="{{ static('core/js/jszip/jszip.min.js') }}"></script>
|
||||
<script defer src="{{ static('core/js/jszip/jszip-utils.min.js') }}"></script>
|
||||
<script defer type="module">
|
||||
import { showSaveFilePicker } from "{{ static('core/js/native-file-system-adapter/mod.js') }}";
|
||||
window.showSaveFilePicker = showSaveFilePicker; /* Export function to normal javascript */
|
||||
</script>
|
||||
<script defer type="text/javascript" src="{{ static('core/js/zipjs/zip-fs-full.min.js') }}"></script>
|
||||
<script defer src="{{ static("core/js/alpinejs.min.js") }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
@ -19,11 +19,22 @@
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
{% if can_edit(profile, user) %}
|
||||
<button disabled id="download" onclick="download('{{ url('api:pictures') }}?users_identified={{ object.id }}')">{% trans %}Download all my pictures{% endtrans %}</button>
|
||||
{% if user.id == object.id and albums|length > 0 %}
|
||||
<div x-data="picture_download" x-cloak>
|
||||
<button
|
||||
:disabled="in_progress"
|
||||
class="btn btn-blue"
|
||||
@click="download('{{ url("api:pictures") }}?users_identified={{ object.id }}')"
|
||||
>
|
||||
<i class="fa fa-download"></i>
|
||||
{% trans %}Download all my pictures{% endtrans %}
|
||||
</button>
|
||||
<progress x-ref="progress" x-show="in_progress"></progress>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for album, pictures in albums|items %}
|
||||
<h4>{{ album }}</h4>
|
||||
<br />
|
||||
<div class="photos">
|
||||
{% for picture in pictures %}
|
||||
{% if picture.can_be_viewed_by(user) %}
|
||||
@ -51,52 +62,62 @@
|
||||
</div>
|
||||
<br>
|
||||
{% endfor %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
/* Enable button once everything is loaded and if JSZip is supported */
|
||||
document.getElementById("download").disabled = !JSZip.support.blob;
|
||||
});
|
||||
async function download(url) {
|
||||
|
||||
let zip = new JSZip();
|
||||
let size = 0;
|
||||
let pictures = await (await fetch(url)).json();
|
||||
pictures.forEach(async (picture) => {
|
||||
size += picture.size;
|
||||
zip.file(
|
||||
"IMG_" + picture.date + picture.name.slice(picture.name.lastIndexOf(".")),
|
||||
new Promise(function (resolve, reject) {
|
||||
JSZipUtils.getBinaryContent(picture.full_size_url, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(data);
|
||||
})
|
||||
}),
|
||||
{ binary: true }
|
||||
);
|
||||
});
|
||||
|
||||
let fileHandle = await window.showSaveFilePicker({
|
||||
_preferPolyfill: false,
|
||||
suggestedName: "{%- trans -%} pictures {%- endtrans -%}.zip",
|
||||
types: {},
|
||||
excludeAcceptAllOption: false,
|
||||
})
|
||||
let writeStream = await fileHandle.createWritable();
|
||||
|
||||
await zip.generateInternalStream({
|
||||
type: "uint8array",
|
||||
streamFiles: true,
|
||||
compression: "DEFLATE",
|
||||
compressionOptions: { level: 9 }
|
||||
})
|
||||
.on("data", (data) => writeStream.write(data))
|
||||
.on("error", (err) => console.error(err))
|
||||
.on("end", () => writeStream.close())
|
||||
.resume();
|
||||
}
|
||||
</script>
|
||||
</main>
|
||||
{% endblock %}
|
||||
{% endblock content %}
|
||||
|
||||
{% block script %}
|
||||
|
||||
{{ super() }}
|
||||
{% if user.id == object.id %}
|
||||
<script>
|
||||
/**
|
||||
* @typedef Picture
|
||||
* @property {number} id
|
||||
* @property {string} name
|
||||
* @property {number} size
|
||||
* @property {string} date
|
||||
* @property {Object} author
|
||||
* @property {string} full_size_url
|
||||
* @property {string} compressed_url
|
||||
* @property {string} thumb_url
|
||||
* @property {string} album
|
||||
*/
|
||||
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.data("picture_download", () => ({
|
||||
in_progress: false,
|
||||
|
||||
async download(url) {
|
||||
this.in_progress = true;
|
||||
const bar = this.$refs.progress;
|
||||
bar.value = 0;
|
||||
|
||||
/** @type Picture[] */
|
||||
const pictures = await (await fetch(url)).json();
|
||||
bar.max = pictures.length;
|
||||
|
||||
const fileHandle = await window.showSaveFilePicker({
|
||||
_preferPolyfill: false,
|
||||
suggestedName: "{%- trans -%} pictures {%- endtrans -%}.zip",
|
||||
types: {},
|
||||
excludeAcceptAllOption: false,
|
||||
})
|
||||
const zipWriter = new zip.ZipWriter(await fileHandle.createWritable());
|
||||
|
||||
await Promise.all(pictures.map(p => {
|
||||
const img_name = p.album + "/IMG_" + p.date.replaceAll(/[:\-]/g, "_") + p.name.slice(p.name.lastIndexOf("."));
|
||||
return zipWriter.add(
|
||||
img_name,
|
||||
new zip.HttpReader(p.full_size_url),
|
||||
{level: 9, lastModDate: new Date(p.date), onstart: () => bar.value += 1}
|
||||
);
|
||||
}));
|
||||
|
||||
await zipWriter.close();
|
||||
this.in_progress = false;
|
||||
}
|
||||
}))
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock script %}
|
||||
|
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
@ -41,7 +40,7 @@ class CustomerAdmin(SearchModelAdmin):
|
||||
"user__first_name",
|
||||
"user__last_name",
|
||||
)
|
||||
form = make_ajax_form(Customer, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(BillingInfo)
|
||||
@ -52,18 +51,13 @@ class BillingInfoAdmin(admin.ModelAdmin):
|
||||
@admin.register(Counter)
|
||||
class CounterAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "club", "type")
|
||||
form = make_ajax_form(
|
||||
Counter,
|
||||
{
|
||||
"products": "products",
|
||||
"sellers": "users",
|
||||
},
|
||||
)
|
||||
autocomplete_fields = ("products", "sellers")
|
||||
|
||||
|
||||
@admin.register(Refilling)
|
||||
class RefillingAdmin(SearchModelAdmin):
|
||||
list_display = ("customer", "amount", "counter", "payment_method", "date")
|
||||
autocomplete_fields = ("customer", "operator")
|
||||
search_fields = (
|
||||
"customer__user__username",
|
||||
"customer__user__first_name",
|
||||
@ -71,13 +65,6 @@ class RefillingAdmin(SearchModelAdmin):
|
||||
"customer__account_id",
|
||||
"counter__name",
|
||||
)
|
||||
form = make_ajax_form(
|
||||
Refilling,
|
||||
{
|
||||
"customer": "customers",
|
||||
"operator": "users",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Selling)
|
||||
@ -90,13 +77,7 @@ class SellingAdmin(SearchModelAdmin):
|
||||
"customer__account_id",
|
||||
"counter__name",
|
||||
)
|
||||
form = make_ajax_form(
|
||||
Selling,
|
||||
{
|
||||
"customer": "customers",
|
||||
"seller": "users",
|
||||
},
|
||||
)
|
||||
autocomplete_fields = ("customer", "seller")
|
||||
|
||||
|
||||
@admin.register(Permanency)
|
||||
@ -108,7 +89,7 @@ class PermanencyAdmin(SearchModelAdmin):
|
||||
"user__last_name",
|
||||
"counter__name",
|
||||
)
|
||||
form = make_ajax_form(Permanency, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(ProductType)
|
||||
@ -125,7 +106,7 @@ class CashRegisterSummaryAdmin(SearchModelAdmin):
|
||||
"user__last_name",
|
||||
"counter__name",
|
||||
)
|
||||
form = make_ajax_form(CashRegisterSummary, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(Eticket)
|
||||
|
@ -16,12 +16,15 @@ import json
|
||||
import re
|
||||
import string
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import timedelta
|
||||
from model_bakery import baker
|
||||
|
||||
from club.models import Club
|
||||
from club.models import Club, Membership
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import User
|
||||
from counter.models import BillingInfo, Counter, Customer, Permanency, Product, Selling
|
||||
from sith.settings import SITH_MAIN_CLUB
|
||||
@ -911,3 +914,47 @@ class TestCustomerAccountId(TestCase):
|
||||
assert created is False
|
||||
assert account.account_id == "1111a"
|
||||
assert account.amount == 10
|
||||
|
||||
|
||||
class TestClubCounterClickAccess(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.counter = baker.make(Counter, type="OFFICE")
|
||||
cls.customer = subscriber_user.make()
|
||||
cls.counter_url = reverse(
|
||||
"counter:details", kwargs={"counter_id": cls.counter.id}
|
||||
)
|
||||
cls.click_url = reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": cls.counter.id, "user_id": cls.customer.id},
|
||||
)
|
||||
|
||||
cls.user = subscriber_user.make()
|
||||
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
|
||||
def test_anonymous(self):
|
||||
res = self.client.get(self.click_url)
|
||||
assert res.status_code == 403
|
||||
|
||||
def test_logged_in_without_rights(self):
|
||||
self.client.force_login(self.user)
|
||||
res = self.client.get(self.click_url)
|
||||
assert res.status_code == 403
|
||||
# being a member of the club, without being in the board, isn't enough
|
||||
baker.make(Membership, club=self.counter.club, user=self.user, role=1)
|
||||
res = self.client.get(self.click_url)
|
||||
assert res.status_code == 403
|
||||
|
||||
def test_board_member(self):
|
||||
baker.make(Membership, club=self.counter.club, user=self.user, role=3)
|
||||
self.client.force_login(self.user)
|
||||
res = self.client.get(self.click_url)
|
||||
assert res.status_code == 200
|
||||
|
||||
def test_barman(self):
|
||||
self.counter.sellers.add(self.user)
|
||||
self.client.force_login(self.user)
|
||||
res = self.client.get(self.click_url)
|
||||
assert res.status_code == 200
|
||||
|
@ -329,7 +329,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
raise Http404
|
||||
if obj.type != "BAR" and not request.user.is_authenticated:
|
||||
raise PermissionDenied
|
||||
if (
|
||||
if obj.type == "BAR" and (
|
||||
"counter_token" not in request.session
|
||||
or request.session["counter_token"] != obj.token
|
||||
or len(obj.barmen_list) == 0
|
||||
|
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
|
||||
from eboutic.models import *
|
||||
@ -21,7 +20,7 @@ from eboutic.models import *
|
||||
@admin.register(Basket)
|
||||
class BasketAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "date", "get_total")
|
||||
form = make_ajax_form(Basket, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(BasketItem)
|
||||
@ -34,7 +33,7 @@ class BasketItemAdmin(admin.ModelAdmin):
|
||||
class InvoiceAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "date", "validated")
|
||||
search_fields = ("user__username", "user__first_name", "user__last_name")
|
||||
form = make_ajax_form(Invoice, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(InvoiceItem)
|
||||
|
@ -136,7 +136,7 @@
|
||||
right: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0px 0px 12px 2px rgb(0 0 0 / 14%);
|
||||
box-shadow: 0 0 12px 2px rgb(0 0 0 / 14%);
|
||||
background-color: white;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@ -186,29 +186,7 @@
|
||||
}
|
||||
|
||||
#eboutic .catalog-buttons button {
|
||||
font-size: 15px!important;
|
||||
font-weight: normal;
|
||||
color: white;
|
||||
min-width: 60px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
#eboutic .catalog-buttons .validate {
|
||||
background-color: #354a5f;
|
||||
}
|
||||
#eboutic .catalog-buttons .clear {
|
||||
background-color: gray;
|
||||
}
|
||||
#eboutic .catalog-buttons button i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#eboutic .catalog-buttons button.validate:hover {
|
||||
background-color: #2c3646;
|
||||
}
|
||||
|
||||
#eboutic .catalog-buttons button.clear:hover {
|
||||
background-color:hsl(210,5%,30%);
|
||||
}
|
||||
|
||||
#eboutic .catalog-buttons form {
|
||||
@ -252,7 +230,7 @@
|
||||
}
|
||||
|
||||
#eboutic .product-image {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: 0;
|
||||
max-width: 70px;
|
||||
}
|
||||
}
|
||||
|
@ -62,13 +62,13 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div class="catalog-buttons">
|
||||
<button @click="clear_basket()" class="clear">
|
||||
<button @click="clear_basket()" class="btn btn-grey">
|
||||
<i class="fa fa-trash"></i>
|
||||
{% trans %}Clear{% endtrans %}
|
||||
</button>
|
||||
<form method="get" action="{{ url('eboutic:command') }}">
|
||||
{% csrf_token %}
|
||||
<button class="validate">
|
||||
<button class="btn btn-blue">
|
||||
<i class="fa fa-check"></i>
|
||||
<input type="submit" value="{% trans %}Validate{% endtrans %}"/>
|
||||
</button>
|
||||
|
@ -1,4 +1,3 @@
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
|
||||
from election.models import Candidature, Election, ElectionList, Role
|
||||
@ -13,7 +12,7 @@ class ElectionAdmin(admin.ModelAdmin):
|
||||
"is_vote_finished",
|
||||
"archived",
|
||||
)
|
||||
form = make_ajax_form(Election, {"voters": "users"})
|
||||
autocomplete_fields = ("voters",)
|
||||
|
||||
|
||||
@admin.register(Role)
|
||||
@ -31,7 +30,7 @@ class ElectionListAdmin(admin.ModelAdmin):
|
||||
@admin.register(Candidature)
|
||||
class CandidatureAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "role", "election_list")
|
||||
form = make_ajax_form(Candidature, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
# Votes must stay fully anonymous, so no ModelAdmin for Vote model
|
||||
|
@ -19,19 +19,16 @@ from haystack.admin import SearchModelAdmin
|
||||
from forum.models import *
|
||||
|
||||
|
||||
@admin.register(Forum)
|
||||
class ForumAdmin(SearchModelAdmin):
|
||||
search_fields = ["name", "description"]
|
||||
|
||||
|
||||
@admin.register(ForumTopic)
|
||||
class ForumTopicAdmin(SearchModelAdmin):
|
||||
search_fields = ["_title", "description"]
|
||||
|
||||
|
||||
@admin.register(ForumMessage)
|
||||
class ForumMessageAdmin(SearchModelAdmin):
|
||||
search_fields = ["title", "message"]
|
||||
|
||||
|
||||
admin.site.register(Forum, ForumAdmin)
|
||||
admin.site.register(ForumTopic, ForumTopicAdmin)
|
||||
admin.site.register(ForumMessage, ForumMessageAdmin)
|
||||
admin.site.register(ForumUserInfo)
|
||||
|
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
|
||||
from launderette.models import *
|
||||
@ -31,10 +30,10 @@ class MachineAdmin(admin.ModelAdmin):
|
||||
@admin.register(Token)
|
||||
class TokenAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "launderette", "type", "user")
|
||||
form = make_ajax_form(Token, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
||||
|
||||
@admin.register(Slot)
|
||||
class SlotAdmin(admin.ModelAdmin):
|
||||
list_display = ("machine", "user", "start_date")
|
||||
form = make_ajax_form(Slot, {"user": "users"})
|
||||
autocomplete_fields = ("user",)
|
||||
|
@ -20,7 +20,6 @@
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
@ -31,7 +30,7 @@ from pedagogy.models import UV, UVComment, UVCommentReport
|
||||
class UVAdmin(admin.ModelAdmin):
|
||||
list_display = ("code", "title", "credit_type", "credits", "department")
|
||||
search_fields = ("code", "title", "department")
|
||||
form = make_ajax_form(UV, {"author": "users"})
|
||||
autocomplete_fields = ("author",)
|
||||
|
||||
|
||||
@admin.register(UVComment)
|
||||
@ -43,7 +42,7 @@ class UVCommentAdmin(admin.ModelAdmin):
|
||||
"author__last_name",
|
||||
"uv__code",
|
||||
)
|
||||
form = make_ajax_form(UVComment, {"author": "users"})
|
||||
autocomplete_fields = ("author",)
|
||||
|
||||
|
||||
@admin.register(UVCommentReport)
|
||||
@ -55,4 +54,4 @@ class UVCommentReportAdmin(SearchModelAdmin):
|
||||
"reporter__last_name",
|
||||
"comment__uv__code",
|
||||
)
|
||||
form = make_ajax_form(UVCommentReport, {"reporter": "users"})
|
||||
autocomplete_fields = ("reporter",)
|
||||
|
@ -1,6 +1,8 @@
|
||||
from typing import Literal
|
||||
|
||||
from django.db.models import Q
|
||||
from django.utils import html
|
||||
from haystack.query import SearchQuerySet
|
||||
from ninja import FilterSchema, ModelSchema, Schema
|
||||
from pydantic import AliasPath, ConfigDict, Field, TypeAdapter
|
||||
from pydantic.alias_generators import to_camel
|
||||
@ -120,6 +122,27 @@ class UvFilterSchema(FilterSchema):
|
||||
language: str = "FR"
|
||||
department: set[str] | None = Field(None, q="department__in")
|
||||
|
||||
def filter_search(self, value: str | None) -> Q:
|
||||
"""Special filter for the search text.
|
||||
|
||||
It does a full text search if available.
|
||||
"""
|
||||
if not value:
|
||||
return Q()
|
||||
|
||||
if len(value) < 3 or (len(value) < 5 and any(c.isdigit() for c in value)):
|
||||
# Likely to be an UV code
|
||||
return Q(code__istartswith=value)
|
||||
|
||||
qs = list(
|
||||
SearchQuerySet()
|
||||
.models(UV)
|
||||
.autocomplete(auto=html.escape(value))
|
||||
.values_list("pk", flat=True)
|
||||
)
|
||||
|
||||
return Q(id__in=qs)
|
||||
|
||||
def filter_semester(self, value: set[str] | None) -> Q:
|
||||
"""Special filter for the semester.
|
||||
|
||||
|
@ -2,6 +2,7 @@ import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test.testcases import call_command
|
||||
from django.urls import reverse
|
||||
from model_bakery import baker
|
||||
from model_bakery.recipe import Recipe
|
||||
@ -21,16 +22,31 @@ class TestUVSearch(TestCase):
|
||||
uv_recipe = Recipe(UV, author=cls.root)
|
||||
uvs = [
|
||||
uv_recipe.prepare(
|
||||
code="AP4A", credit_type="CS", semester="AUTUMN", department="GI"
|
||||
code="AP4A",
|
||||
credit_type="CS",
|
||||
semester="AUTUMN",
|
||||
department="GI",
|
||||
manager="francky",
|
||||
title="Programmation Orientée Objet: Concepts fondamentaux et mise en pratique avec le langage C++",
|
||||
),
|
||||
uv_recipe.prepare(
|
||||
code="MT01", credit_type="CS", semester="AUTUMN", department="TC"
|
||||
code="MT01",
|
||||
credit_type="CS",
|
||||
semester="AUTUMN",
|
||||
department="TC",
|
||||
manager="ben",
|
||||
title="Intégration1. Algèbre linéaire - Fonctions de deux variables",
|
||||
),
|
||||
uv_recipe.prepare(
|
||||
code="PHYS11", credit_type="CS", semester="AUTUMN", department="TC"
|
||||
),
|
||||
uv_recipe.prepare(
|
||||
code="TNEV", credit_type="TM", semester="SPRING", department="TC"
|
||||
code="TNEV",
|
||||
credit_type="TM",
|
||||
semester="SPRING",
|
||||
department="TC",
|
||||
manager="moss",
|
||||
title="tnetennba",
|
||||
),
|
||||
uv_recipe.prepare(
|
||||
code="MT10", credit_type="TM", semester="AUTUMN", department="IMSI"
|
||||
@ -40,9 +56,11 @@ class TestUVSearch(TestCase):
|
||||
credit_type="TM",
|
||||
semester="AUTUMN_AND_SPRING",
|
||||
department="GI",
|
||||
manager="francky",
|
||||
),
|
||||
]
|
||||
UV.objects.bulk_create(uvs)
|
||||
call_command("update_index")
|
||||
|
||||
def test_permissions(self):
|
||||
# Test with anonymous user
|
||||
@ -92,14 +110,22 @@ class TestUVSearch(TestCase):
|
||||
],
|
||||
}
|
||||
|
||||
def test_search_by_code(self):
|
||||
def test_search_by_text(self):
|
||||
self.client.force_login(self.root)
|
||||
res = self.client.get(self.url + "?search=MT")
|
||||
assert res.status_code == 200
|
||||
assert {uv["code"] for uv in json.loads(res.content)["results"]} == {
|
||||
"MT01",
|
||||
"MT10",
|
||||
}
|
||||
for query, expected in (
|
||||
# UV code search case insensitive
|
||||
("m", {"MT01", "MT10"}),
|
||||
("M", {"MT01", "MT10"}),
|
||||
("mt", {"MT01", "MT10"}),
|
||||
("MT", {"MT01", "MT10"}),
|
||||
("algèbre", {"MT01"}), # Title search case insensitive
|
||||
# Manager search
|
||||
("moss", {"TNEV"}),
|
||||
("francky", {"DA50", "AP4A"}),
|
||||
):
|
||||
res = self.client.get(self.url + f"?search={query}")
|
||||
assert res.status_code == 200
|
||||
assert {uv["code"] for uv in json.loads(res.content)["results"]} == expected
|
||||
|
||||
def test_search_by_credit_type(self):
|
||||
self.client.force_login(self.root)
|
||||
|
152
poetry.lock
generated
152
poetry.lock
generated
@ -57,6 +57,17 @@ six = ">=1.12.0"
|
||||
astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
|
||||
test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "4.0.3"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
|
||||
{file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "babel"
|
||||
version = "2.15.0"
|
||||
@ -499,18 +510,14 @@ bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "django-ajax-selects"
|
||||
version = "3.0.2"
|
||||
version = "2.2.1"
|
||||
description = "Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete."
|
||||
optional = false
|
||||
python-versions = ">=3.10,<4.0"
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "django_ajax_selects-3.0.2-py3-none-any.whl", hash = "sha256:83da065b3fe6bdee5996662734eb18ee70fee510171c179a02f1ce45dcaa2870"},
|
||||
{file = "django_ajax_selects-3.0.2.tar.gz", hash = "sha256:8554659f5c7da50cfe1f0d0e14c6f360d0f2ab2d94b24e3203cc4fe974bd945a"},
|
||||
{file = "django-ajax-selects-2.2.1.tar.gz", hash = "sha256:996ffb38dff1a621b358613afdf2681dbf261e5976da3c30a75e9b08fd81a887"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "django-countries"
|
||||
version = "7.6.1"
|
||||
@ -821,6 +828,109 @@ files = [
|
||||
backports-strenum = {version = ">=1.3", markers = "python_version < \"3.11\""}
|
||||
colorama = ">=0.4"
|
||||
|
||||
[[package]]
|
||||
name = "hiredis"
|
||||
version = "3.0.0"
|
||||
description = "Python wrapper for hiredis"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "hiredis-3.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:4b182791c41c5eb1d9ed736f0ff81694b06937ca14b0d4dadde5dadba7ff6dae"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:13c275b483a052dd645eb2cb60d6380f1f5215e4c22d6207e17b86be6dd87ffa"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1018cc7f12824506f165027eabb302735b49e63af73eb4d5450c66c88f47026"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83a29cc7b21b746cb6a480189e49f49b2072812c445e66a9e38d2004d496b81c"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e241fab6332e8fb5f14af00a4a9c6aefa22f19a336c069b7ddbf28ef8341e8d6"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1fb8de899f0145d6c4d5d4bd0ee88a78eb980a7ffabd51e9889251b8f58f1785"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b23291951959141173eec10f8573538e9349fa27f47a0c34323d1970bf891ee5"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e421ac9e4b5efc11705a0d5149e641d4defdc07077f748667f359e60dc904420"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77c8006c12154c37691b24ff293c077300c22944018c3ff70094a33e10c1d795"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:41afc0d3c18b59eb50970479a9c0e5544fb4b95e3a79cf2fbaece6ddefb926fe"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:04ccae6dcd9647eae6025425ab64edb4d79fde8b9e6e115ebfabc6830170e3b2"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fe91d62b0594db5ea7d23fc2192182b1a7b6973f628a9b8b2e0a42a2be721ac6"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99516d99316062824a24d145d694f5b0d030c80da693ea6f8c4ecf71a251d8bb"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-win32.whl", hash = "sha256:562eaf820de045eb487afaa37e6293fe7eceb5b25e158b5a1974b7e40bf04543"},
|
||||
{file = "hiredis-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a1c81c89ed765198da27412aa21478f30d54ef69bf5e4480089d9c3f77b8f882"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:4664dedcd5933364756d7251a7ea86d60246ccf73a2e00912872dacbfcef8978"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:47de0bbccf4c8a9f99d82d225f7672b9dd690d8fd872007b933ef51a302c9fa6"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e43679eca508ba8240d016d8cca9d27342d70184773c15bea78a23c87a1922f1"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13c345e7278c210317e77e1934b27b61394fee0dec2e8bd47e71570900f75823"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00018f22f38530768b73ea86c11f47e8d4df65facd4e562bd78773bd1baef35e"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ea3a86405baa8eb0d3639ced6926ad03e07113de54cb00fd7510cb0db76a89d"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c073848d2b1d5561f3903879ccf4e1a70c9b1e7566c7bdcc98d082fa3e7f0a1d"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a8dffb5f5b3415a4669d25de48b617fd9d44b0bccfc4c2ab24b06406ecc9ecb"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:22c17c96143c2a62dfd61b13803bc5de2ac526b8768d2141c018b965d0333b66"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c3ece960008dab66c6b8bb3a1350764677ee7c74ccd6270aaf1b1caf9ccebb46"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f75999ae00a920f7dce6ecae76fa5e8674a3110e5a75f12c7a2c75ae1af53396"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e069967cbd5e1900aafc4b5943888f6d34937fc59bf8918a1a546cb729b4b1e4"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0aacc0a78e1d94d843a6d191f224a35893e6bdfeb77a4a89264155015c65f126"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-win32.whl", hash = "sha256:719c32147ba29528cb451f037bf837dcdda4ff3ddb6cdb12c4216b0973174718"},
|
||||
{file = "hiredis-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:bdc144d56333c52c853c31b4e2e52cfbdb22d3da4374c00f5f3d67c42158970f"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:484025d2eb8f6348f7876fc5a2ee742f568915039fcb31b478fd5c242bb0fe3a"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fcdb552ffd97151dab8e7bc3ab556dfa1512556b48a367db94b5c20253a35ee1"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bb6f9fd92f147ba11d338ef5c68af4fd2908739c09e51f186e1d90958c68cc1"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa86bf9a0ed339ec9e8a9a9d0ae4dccd8671625c83f9f9f2640729b15e07fbfd"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e194a0d5df9456995d8f510eab9f529213e7326af6b94770abf8f8b7952ddcaa"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a1df39d74ec507d79c7a82c8063eee60bf80537cdeee652f576059b9cdd15c"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f91456507427ba36fd81b2ca11053a8e112c775325acc74e993201ea912d63e9"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9862db92ef67a8a02e0d5370f07d380e14577ecb281b79720e0d7a89aedb9ee5"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d10fcd9e0eeab835f492832b2a6edb5940e2f1230155f33006a8dfd3bd2c94e4"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:48727d7d405d03977d01885f317328dc21d639096308de126c2c4e9950cbd3c9"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e0bb6102ebe2efecf8a3292c6660a0e6fac98176af6de67f020bea1c2343717"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:df274e3abb4df40f4c7274dd3e587dfbb25691826c948bc98d5fead019dfb001"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:034925b5fb514f7b11aac38cd55b3fd7e9d3af23bd6497f3f20aa5b8ba58e232"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-win32.whl", hash = "sha256:120f2dda469b28d12ccff7c2230225162e174657b49cf4cd119db525414ae281"},
|
||||
{file = "hiredis-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:e584fe5f4e6681d8762982be055f1534e0170f6308a7a90f58d737bab12ff6a8"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:122171ff47d96ed8dd4bba6c0e41d8afaba3e8194949f7720431a62aa29d8895"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ba9fc605ac558f0de67463fb588722878641e6fa1dabcda979e8e69ff581d0bd"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a631e2990b8be23178f655cae8ac6c7422af478c420dd54e25f2e26c29e766f1"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63482db3fadebadc1d01ad33afa6045ebe2ea528eb77ccaabd33ee7d9c2bad48"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f669212c390eebfbe03c4e20181f5970b82c5d0a0ad1df1785f7ffbe7d61150"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a49ef161739f8018c69b371528bdb47d7342edfdee9ddc75a4d8caddf45a6e"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98a152052b8878e5e43a2e3a14075218adafc759547c98668a21e9485882696c"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50a196af0ce657fcde9bf8a0bbe1032e22c64d8fcec2bc926a35e7ff68b3a166"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f2f312eef8aafc2255e3585dcf94d5da116c43ef837db91db9ecdc1bc930072d"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:6ca41fa40fa019cde42c21add74aadd775e71458051a15a352eabeb12eb4d084"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6eecb343c70629f5af55a8b3e53264e44fa04e155ef7989de13668a0cb102a90"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:c3fdad75e7837a475900a1d3a5cc09aa024293c3b0605155da2d42f41bc0e482"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8854969e7480e8d61ed7549eb232d95082a743e94138d98d7222ba4e9f7ecacd"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-win32.whl", hash = "sha256:f114a6c86edbf17554672b050cce72abf489fe58d583c7921904d5f1c9691605"},
|
||||
{file = "hiredis-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:7d99b91e42217d7b4b63354b15b41ce960e27d216783e04c4a350224d55842a4"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:4c6efcbb5687cf8d2aedcc2c3ed4ac6feae90b8547427d417111194873b66b06"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5b5cff42a522a0d81c2ae7eae5e56d0ee7365e0c4ad50c4de467d8957aff4414"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:82f794d564f4bc76b80c50b03267fe5d6589e93f08e66b7a2f674faa2fa76ebc"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7a4c1791d7aa7e192f60fe028ae409f18ccdd540f8b1e6aeb0df7816c77e4a4"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2537b2cd98192323fce4244c8edbf11f3cac548a9d633dbbb12b48702f379f4"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fed69bbaa307040c62195a269f82fc3edf46b510a17abb6b30a15d7dab548df"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869f6d5537d243080f44253491bb30aa1ec3c21754003b3bddeadedeb65842b0"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d435ae89073d7cd51e6b6bf78369c412216261c9c01662e7008ff00978153729"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:204b79b30a0e6be0dc2301a4d385bb61472809f09c49f400497f1cdd5a165c66"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ea635101b739c12effd189cc19b2671c268abb03013fd1f6321ca29df3ca625"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f359175197fd833c8dd7a8c288f1516be45415bb5c939862ab60c2918e1e1943"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac6d929cb33dd12ad3424b75725975f0a54b5b12dbff95f2a2d660c510aa106d"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:100431e04d25a522ef2c3b94f294c4219c4de3bfc7d557b6253296145a144c11"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-win32.whl", hash = "sha256:e1a9c14ae9573d172dc050a6f63a644457df5d01ec4d35a6a0f097f812930f83"},
|
||||
{file = "hiredis-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:54a6dd7b478e6eb01ce15b3bb5bf771e108c6c148315bf194eb2ab776a3cac4d"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:50da7a9edf371441dfcc56288d790985ee9840d982750580710a9789b8f4a290"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b285ef6bf1581310b0d5e8f6ce64f790a1c40e89c660e1320b35f7515433672"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dcfa684966f25b335072115de2f920228a3c2caf79d4bfa2b30f6e4f674a948"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a41be8af1fd78ca97bc948d789a09b730d1e7587d07ca53af05758f31f4b985d"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:038756db735e417ab36ee6fd7725ce412385ed2bd0767e8179a4755ea11b804f"},
|
||||
{file = "hiredis-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fcecbd39bd42cef905c0b51c9689c39d0cc8b88b1671e7f40d4fb213423aef3a"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a131377493a59fb0f5eaeb2afd49c6540cafcfba5b0b3752bed707be9e7c4eaf"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d22c53f0ec5c18ecb3d92aa9420563b1c5d657d53f01356114978107b00b860"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a91e9520fbc65a799943e5c970ffbcd67905744d8becf2e75f9f0a5e8414f0"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dc8043959b50141df58ab4f398e8ae84c6f9e673a2c9407be65fc789138f4a6"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51b99cfac514173d7b8abdfe10338193e8a0eccdfe1870b646009d2fb7cbe4b5"},
|
||||
{file = "hiredis-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fa1fcad89d8a41d8dc10b1e54951ec1e161deabd84ed5a2c95c3c7213bdb3514"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:898636a06d9bf575d2c594129085ad6b713414038276a4bfc5db7646b8a5be78"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:466f836dbcf86de3f9692097a7a01533dc9926986022c6617dc364a402b265c5"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23142a8af92a13fc1e3f2ca1d940df3dcf2af1d176be41fe8d89e30a837a0b60"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:793c80a3d6b0b0e8196a2d5de37a08330125668c8012922685e17aa9108c33ac"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:467d28112c7faa29b7db743f40803d927c8591e9da02b6ce3d5fadc170a542a2"},
|
||||
{file = "hiredis-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dc384874a719c767b50a30750f937af18842ee5e288afba95a5a3ed703b1515a"},
|
||||
{file = "hiredis-3.0.0.tar.gz", hash = "sha256:fed8581ae26345dea1f1e0d1a96e05041a727a45e7d8d459164583e23c6ac441"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.6.0"
|
||||
@ -1483,13 +1593,13 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.7.1"
|
||||
version = "3.8.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"},
|
||||
{file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"},
|
||||
{file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
|
||||
{file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1892,6 +2002,7 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
@ -1940,6 +2051,25 @@ files = [
|
||||
[package.dependencies]
|
||||
pyyaml = "*"
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.8"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
|
||||
{file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""}
|
||||
hiredis = {version = ">1.0.0", optional = true, markers = "extra == \"hiredis\""}
|
||||
|
||||
[package.extras]
|
||||
hiredis = ["hiredis (>1.0.0)"]
|
||||
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2024.7.24"
|
||||
@ -2502,4 +2632,4 @@ filelock = ">=3.4"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "5e90eff0d3e11e48c1467165d121f1cff094cf83834b5df517708e72bc82425f"
|
||||
content-hash = "a9573a584420b00b0bd5bb85a0fb2daedb365bd1ff604b94ec23c187bc4dd991"
|
||||
|
@ -30,7 +30,7 @@ django-jinja = "^2.11"
|
||||
cryptography = "^43.0.0"
|
||||
django-phonenumber-field = "^8.0.0"
|
||||
phonenumbers = "^8.12"
|
||||
django-ajax-selects = "^3.0.2"
|
||||
django-ajax-selects = "^2.2.1"
|
||||
reportlab = "^4.2"
|
||||
django-haystack = "^3.2.1"
|
||||
xapian-haystack = "^3.0.1"
|
||||
@ -50,6 +50,7 @@ django-honeypot = "^1.2.0"
|
||||
[tool.poetry.group.prod.dependencies]
|
||||
# deps used in prod, but unnecessary for development
|
||||
psycopg2-binary = "^2.9"
|
||||
redis = {extras = ["hiredis"], version = "^5.0.8"}
|
||||
|
||||
[tool.poetry.group.prod]
|
||||
optional = true
|
||||
@ -58,7 +59,7 @@ optional = true
|
||||
# deps used for development purposes, but unneeded in prod
|
||||
django-debug-toolbar = "^4.4.6"
|
||||
ipython = "^8.26.0"
|
||||
pre-commit = "^3.7.1"
|
||||
pre-commit = "^3.8.0"
|
||||
ruff = "^0.5.5" # Version used in pipeline is controlled by pre-commit hooks in .pre-commit.config.yaml
|
||||
djhtml = "^3.0.6"
|
||||
faker = "^26.0.0"
|
||||
|
18
sas/admin.py
18
sas/admin.py
@ -15,8 +15,20 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from sas.models import *
|
||||
from sas.models import Album, PeoplePictureRelation, Picture
|
||||
|
||||
|
||||
@admin.register(Picture)
|
||||
class PictureAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "parent", "date", "size", "is_moderated")
|
||||
search_fields = ("name",)
|
||||
autocomplete_fields = ("owner", "parent", "edit_groups", "view_groups", "moderator")
|
||||
|
||||
|
||||
@admin.register(PeoplePictureRelation)
|
||||
class PeoplePictureRelationAdmin(admin.ModelAdmin):
|
||||
list_display = ("picture", "user")
|
||||
autocomplete_fields = ("picture", "user")
|
||||
|
||||
|
||||
admin.site.register(Album)
|
||||
# admin.site.register(Picture)
|
||||
admin.site.register(PeoplePictureRelation)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.db.models import F
|
||||
from ninja import Query
|
||||
from ninja_extra import ControllerBase, api_controller, route
|
||||
from ninja_extra.exceptions import PermissionDenied
|
||||
@ -47,6 +48,7 @@ class PicturesController(ControllerBase):
|
||||
)
|
||||
.distinct()
|
||||
.order_by("-date")
|
||||
.annotate(album=F("parent__name"))
|
||||
)
|
||||
for picture in pictures:
|
||||
picture.full_size_url = picture.get_download_url()
|
||||
|
@ -23,6 +23,7 @@ class PictureSchema(ModelSchema):
|
||||
full_size_url: str
|
||||
compressed_url: str
|
||||
thumb_url: str
|
||||
album: str
|
||||
|
||||
|
||||
class PictureCreateRelationSchema(Schema):
|
||||
|
@ -203,8 +203,6 @@ SASS_PRECISION = 8
|
||||
|
||||
WSGI_APPLICATION = "sith.wsgi.application"
|
||||
|
||||
REST_FRAMEWORK = {}
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
|
||||
@ -215,6 +213,8 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
|
||||
|
||||
# Logging
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
@ -280,7 +280,7 @@ LOGOUT_URL = "/logout"
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
DEFAULT_FROM_EMAIL = "bibou@git.an"
|
||||
SITH_COM_EMAIL = "bibou_com@git.an"
|
||||
REST_FRAMEWORK["UNAUTHENTICATED_USER"] = "core.models.AnonymousUser"
|
||||
|
||||
# Those values are to be changed in production to be more effective
|
||||
HONEYPOT_FIELD_NAME = "body2"
|
||||
HONEYPOT_VALUE = "content"
|
||||
@ -724,8 +724,7 @@ if SENTRY_DSN:
|
||||
)
|
||||
|
||||
SITH_FRONT_DEP_VERSIONS = {
|
||||
"https://github.com/Stuk/jszip-utils": "0.1.0",
|
||||
"https://github.com/Stuk/jszip": "3.10.1",
|
||||
"https://github.com/gildas-lormeau/zip.js": "2.7.47",
|
||||
"https://github.com/jimmywarting/native-file-system-adapter": "3.0.1",
|
||||
"https://github.com/chartjs/Chart.js/": "2.6.0",
|
||||
"https://github.com/Ionaru/easy-markdown-editor/": "2.18.0",
|
||||
|
@ -12,7 +12,6 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
from ajax_select import make_ajax_form
|
||||
from django.contrib import admin
|
||||
|
||||
from subscription.models import Subscription
|
||||
@ -33,4 +32,4 @@ class SubscriptionAdmin(admin.ModelAdmin):
|
||||
"subscription_end",
|
||||
"subscription_type",
|
||||
)
|
||||
form = make_ajax_form(Subscription, {"member": "users"})
|
||||
autocomplete_fields = ("member",)
|
||||
|
Loading…
Reference in New Issue
Block a user