Browse Source

v1.0.0

main
scayac 6 months ago
parent
commit
e2b4b24cd6
  1. 2
      ia_prof/settings.py
  2. 18
      main/admin.py
  3. 15
      main/models.py
  4. 38
      main/templates/export_pdf.js
  5. 87
      main/templates/resultat_appreciations.html
  6. 24
      main/views.py

2
ia_prof/settings.py

@ -113,4 +113,4 @@ STATIC_URL = '/static/' @@ -113,4 +113,4 @@ STATIC_URL = '/static/'
# https://docs.djangoproject.com/en/4.x/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
OPENAI_API_KEY = "sk-proj-hrV9Se3D3Vn6ro66AoMFT3BlbkFJ3kgB6P9xQFpcaymQQHFI"
OPENAI_API_KEY = "your-openai-api-key"

18
main/admin.py

@ -1,4 +1,18 @@ @@ -1,4 +1,18 @@
from django.contrib import admin
from .models import UserProfile
from .models import UserProfile, Modele, UserCredit
admin.site.register(UserProfile)
@admin.register(Modele)
class ModeleAdmin(admin.ModelAdmin):
list_display = ('nom', 'code', 'actif')
list_editable = ('code', 'actif')
search_fields = ('nom', 'code')
list_filter = ('actif',)
@admin.register(UserCredit)
class UserCreditAdmin(admin.ModelAdmin):
list_display = ('user', 'credit')
search_fields = ('user__username',)
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user',)

15
main/models.py

@ -7,3 +7,18 @@ class UserProfile(models.Model): @@ -7,3 +7,18 @@ class UserProfile(models.Model):
def __str__(self):
return self.user.username
class Modele(models.Model):
nom = models.CharField(max_length=255)
code = models.CharField(max_length=255, unique=True, default="gpt-4.1-mini")
actif = models.BooleanField(default=True)
def __str__(self):
return self.nom
class UserCredit(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='credit')
credit = models.IntegerField(default=10)
def __str__(self):
return f"{self.user.username} - Crédit: {self.credit}"

38
main/templates/export_pdf.js

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
// CDN jsPDF + autoTable pour export PDF
document.addEventListener('DOMContentLoaded', function() {
if (!window.jspdfLoaded) {
const script1 = document.createElement('script');
script1.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
script1.onload = function() {
const script2 = document.createElement('script');
script2.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js';
script2.onload = function() { window.jspdfLoaded = true; };
document.body.appendChild(script2);
};
document.body.appendChild(script1);
}
});
function exportTableToPDF() {
if (!window.jspdfLoaded) {
alert('Les librairies PDF ne sont pas encore chargées. Veuillez réessayer dans quelques secondes.');
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.text('Tableau des appréciations', 14, 14);
const table = document.getElementById('appreciations-table');
const rows = Array.from(table.querySelectorAll('tbody tr'))
.map(tr => [
tr.querySelector('.eleve')?.textContent.trim() || '',
tr.querySelector('.appreciation')?.textContent.trim() || ''
]);
doc.autoTable({
head: [['Élève', 'Appréciation']],
body: rows,
startY: 20,
styles: { fontSize: 10, cellWidth: 'wrap' },
headStyles: { fillColor: [55, 90, 127] }
});
doc.save('appreciations.pdf');
}

87
main/templates/resultat_appreciations.html

@ -64,12 +64,17 @@ body, .bg-light { @@ -64,12 +64,17 @@ body, .bg-light {
[class*="bg-"] {
background-color: inherit !important;
}
#credit-restant {
background-color: #ffe082 !important;
color: #23272b !important;
border: 1px solid #444c56 !important;
}
}
</style>
</head>
<body class="bg-light">
<nav class="navbar navbar-expand navbar-dark bg-dark">
<a class="navbar-brand ps-3" href="#">IAProf</a>
<a class="navbar-brand ps-3" href="/generation/">IAProf</a>
<ul class="navbar-nav ms-auto me-3">
<li class="nav-item">
<a class="nav-link" href="/logout/">Se déconnecter</a>
@ -85,14 +90,29 @@ body, .bg-light { @@ -85,14 +90,29 @@ body, .bg-light {
<h2 class="m-0 font-weight-bold text-primary">Génération des appréciations</h2>
</div>
<div class="card-body">
<div class="mb-3">
<div class="mb-3 d-flex justify-content-between align-items-center">
<div>
<label for="modele-select" class="form-label">Choisir un modèle</label>
<select id="modele-select" class="form-select">
<option value="ft:gpt-4o-2024-08-06:personal:app-gen-gangneux2:AYJecsON">Modèle Gangneux</option>
<option value="gpt-4.1-mini">GPT-4.1 mini</option>
{% for modele in modeles %}
<option value="{{ modele.code }}" {% if modele.code == 'gpt-4.1-mini' %}selected{% endif %}>{{ modele.nom }}</option>
{% endfor %}
{% if not modeles or not modeles|dictsort:'code'|length %}
<option value="gpt-4.1-mini" selected>gpt-4.1-mini</option>
{% endif %}
</select>
</div>
<div>
<span class="badge bg-info text-dark" id="credit-restant">Crédits restants : {{ credit }}</span>
</div>
</div>
<div class="mb-3 d-flex gap-2">
<button id="generation-toggle" class="btn btn-primary mb-3">Lancer la génération</button>
<button id="export-pdf" class="btn btn-secondary mb-3" type="button">Exporter en PDF</button>
</div>
<div id="credit-alert" class="alert alert-danger d-none" role="alert">
Vous n'avez plus de crédits pour générer des appréciations.
</div>
<table class="table table-bordered" id="appreciations-table">
<thead>
<tr>
@ -116,16 +136,18 @@ body, .bg-light { @@ -116,16 +136,18 @@ body, .bg-light {
</tbody>
</table>
<script>
let stopGeneration = false;
let enCours = false;
let stopGeneration = false;
let enCours = false;
function getRowsSansAppreciation() {
function getRowsSansAppreciation() {
return Array.from(document.querySelectorAll('#appreciations-table tbody tr')).filter(row => !row.querySelector('.appreciation').textContent.trim());
}
}
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function() {
const btnToggle = document.getElementById('generation-toggle');
const selectModele = document.getElementById('modele-select');
const creditRestant = document.getElementById('credit-restant');
const creditAlert = document.getElementById('credit-alert');
let rows = getRowsSansAppreciation();
function setButtonState(state) {
@ -151,7 +173,7 @@ body, .bg-light { @@ -151,7 +173,7 @@ body, .bg-light {
}
enCours = true;
setButtonState('arreter');
const row = rows[0]; // Toujours traiter la première ligne sans appréciation
const row = rows[0];
const eleve = row.querySelector('.eleve').textContent;
const appreciationCell = row.querySelector('.appreciation');
appreciationCell.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Génération...';
@ -166,6 +188,17 @@ body, .bg-light { @@ -166,6 +188,17 @@ body, .bg-light {
.then(response => response.json())
.then(data => {
appreciationCell.textContent = data.appreciation;
if (typeof data.credit !== 'undefined') {
creditRestant.textContent = 'Crédits restants : ' + data.credit;
}
if (data.stop) {
stopGeneration = true;
setButtonState('lancer');
if (data.credit === 0) {
creditAlert.classList.remove('d-none');
}
return;
}
traiterLignes();
})
.catch(() => {
@ -175,6 +208,13 @@ body, .bg-light { @@ -175,6 +208,13 @@ body, .bg-light {
}
btnToggle.addEventListener('click', function() {
const credit = parseInt(creditRestant.textContent.replace(/\D/g, ''));
if (credit === 0) {
creditAlert.classList.remove('d-none');
return;
} else {
creditAlert.classList.add('d-none');
}
if (!enCours && (btnToggle.textContent === 'Lancer la génération' || btnToggle.textContent === 'Continuer la génération')) {
stopGeneration = false;
setButtonState('arreter');
@ -186,8 +226,8 @@ body, .bg-light { @@ -186,8 +226,8 @@ body, .bg-light {
});
setButtonState('lancer');
});
</script>
});
</script>
</div>
</div>
</div>
@ -195,5 +235,28 @@ body, .bg-light { @@ -195,5 +235,28 @@ body, .bg-light {
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
<script>
function exportTableToPDF() {
const doc = new window.jspdf.jsPDF();
doc.text('Tableau des appréciations', 14, 14);
const table = document.getElementById('appreciations-table');
const rows = Array.from(table.querySelectorAll('tbody tr'))
.map(tr => [
tr.querySelector('.eleve')?.textContent.trim() || '',
tr.querySelector('.appreciation')?.textContent.trim() || ''
]);
doc.autoTable({
head: [['Élève', 'Appréciation']],
body: rows,
startY: 20,
styles: { fontSize: 10, cellWidth: 'wrap' },
headStyles: { fillColor: [55, 90, 127] }
});
doc.save('appreciations.pdf');
}
document.getElementById('export-pdf').addEventListener('click', exportTableToPDF);
</script>
</body>
</html>

24
main/views.py

@ -5,6 +5,8 @@ from django.conf import settings @@ -5,6 +5,8 @@ from django.conf import settings
from main.openAIAppreciations import *
from django.urls import reverse
from django.http import JsonResponse
from django.contrib.auth.models import User
from main.models import UserCredit, Modele
import os
import uuid
import json
@ -44,7 +46,15 @@ def generation(request): @@ -44,7 +46,15 @@ def generation(request):
@login_required(login_url='/login/')
def resultat_appreciations(request):
appreciations_result = request.session.get('appreciations_json', [])
return render(request, 'resultat_appreciations.html', {'appreciations_json': appreciations_result})
credit = 0
if hasattr(request.user, 'credit'):
credit = request.user.credit.credit
modeles = Modele.objects.filter(actif=True)
return render(request, 'resultat_appreciations.html', {
'appreciations_json': appreciations_result,
'credit': credit,
'modeles': modeles
})
@login_required(login_url='/login/')
def generer_appreciation_ajax(request):
@ -52,8 +62,18 @@ def generer_appreciation_ajax(request): @@ -52,8 +62,18 @@ def generer_appreciation_ajax(request):
data = json.loads(request.body)
eleve = data.get('eleve')
modele = data.get('modele')
user = request.user
# Vérifier le crédit
if hasattr(user, 'credit') and user.credit.credit > 0:
appreciation = generer_appreciation_pour_eleve(eleve, request.session.get('appreciations_json', []), modele)
return JsonResponse({'appreciation': appreciation})
# Décrémenter le crédit
user.credit.credit -= 1
user.credit.save()
credit = user.credit.credit
stop = credit == 0
return JsonResponse({'appreciation': appreciation, 'credit': credit, 'stop': stop})
else:
return JsonResponse({'appreciation': 'Crédit épuisé', 'credit': 0, 'stop': True})
return JsonResponse({'error': 'Méthode non autorisée'}, status=405)
def logout_view(request):

Loading…
Cancel
Save