Skip to content

Commit 9707827

Browse files
feat: test upload with AI summary
1 parent c562660 commit 9707827

File tree

8 files changed

+314
-117
lines changed

8 files changed

+314
-117
lines changed

testreports/admin.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
from django.contrib import admin
2+
from .models import TestReport
23

3-
# Register your models here.
4+
@admin.register(TestReport)
5+
class TestReportAdmin(admin.ModelAdmin):
6+
list_display = ('id', 'user', 'uploaded_file', 'created_on')
7+
list_filter = ('created_on',)
8+
search_fields = ('uploaded_file', 'user__email')
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.2.20 on 2025-08-28 09:55
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
initial = True
11+
12+
dependencies = [
13+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14+
('jobs', '0002_initial'),
15+
]
16+
17+
operations = [
18+
migrations.CreateModel(
19+
name='TestReport',
20+
fields=[
21+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22+
('uploaded_file', models.FileField(upload_to='test_reports/')),
23+
('extracted_text', models.TextField(blank=True, null=True)),
24+
('summary', models.TextField(blank=True, null=True)),
25+
('created_on', models.DateTimeField(auto_now_add=True)),
26+
('linked_job', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='jobs.job')),
27+
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
28+
],
29+
options={
30+
'ordering': ['-created_on'],
31+
},
32+
),
33+
]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 4.2.20 on 2025-08-28 10:02
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('testreports', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='testreport',
15+
name='short_summary',
16+
field=models.CharField(blank=True, max_length=255, null=True),
17+
),
18+
migrations.AddField(
19+
model_name='testreport',
20+
name='test_type',
21+
field=models.CharField(blank=True, max_length=100, null=True),
22+
),
23+
]

testreports/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class TestReport(models.Model):
99
summary = models.TextField(blank=True, null=True)
1010
created_on = models.DateTimeField(auto_now_add=True)
1111
linked_job = models.ForeignKey(Job, on_delete=models.SET_NULL, null=True, blank=True)
12+
test_type = models.CharField(max_length=100, blank=True, null=True) # t.ex. "Personlighetstest"
13+
short_summary = models.CharField(max_length=255, blank=True, null=True) # 1 mening
1214

1315
def __str__(self):
1416
return f"Report #{self.id}{self.uploaded_file.name}"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{% extends 'base.html' %}
2+
{% block title %}Test Report Details{% endblock %}
3+
4+
{% block content %}
5+
<div class="container-xl mt-5">
6+
<h1 class="mb-4">
7+
<i class="fa-solid fa-file-lines"></i> Test Report #{{ report.id }}
8+
</h1>
9+
10+
<div class="mb-3">
11+
<strong>Uploaded file:</strong>
12+
<a href="{{ report.uploaded_file.url }}" target="_blank">{{ report.uploaded_file.name }}</a>
13+
</div>
14+
15+
<div class="mb-3">
16+
<strong>Uploaded on:</strong> {{ report.created_on|date:"Y-m-d H:i" }}
17+
</div>
18+
19+
<div class="mb-3">
20+
<strong>Linked job:</strong>
21+
{% if report.linked_job %}
22+
{{ report.linked_job.title }}
23+
{% else %}
24+
<span class="text-muted">No job linked</span>
25+
{% endif %}
26+
</div>
27+
28+
<hr>
29+
30+
<h4>AI Summary:</h4>
31+
{% if report.summary %}
32+
<div class="bg-light p-3 border rounded">
33+
{{ report.summary|linebreaks }}
34+
</div>
35+
{% else %}
36+
<p class="text-muted fst-italic">No AI summary available for this report.</p>
37+
{% endif %}
38+
39+
<hr>
40+
41+
<h4>Extracted text (raw):</h4>
42+
{% if report.extracted_text %}
43+
<pre class="bg-dark text-white p-3 rounded" style="white-space: pre-wrap;">{{ report.extracted_text }}</pre>
44+
{% else %}
45+
<p class="text-muted fst-italic">No text extracted from this report.</p>
46+
{% endif %}
47+
48+
<a href="{% url 'your_testreports' %}" class="btn btn-secondary mt-4">Back to Reports</a>
49+
</div>
50+
{% endblock %}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{% extends 'base.html' %}
2+
{% block title %}Upload Test Report{% endblock %}
3+
4+
{% block content %}
5+
<div class="container-xl mt-5">
6+
<h1 class="mb-4"><i class="fa-regular fa-file-pdf"></i> Upload a Test Report</h1>
7+
8+
<form method="POST" enctype="multipart/form-data">
9+
{% csrf_token %}
10+
<div class="mb-3">
11+
<label for="uploaded_file" class="form-label">Select a test report file (PDF, Word, etc):</label>
12+
<input type="file" name="uploaded_file" id="uploaded_file" class="form-control" required>
13+
</div>
14+
15+
<button type="submit" class="btn btn-primary">
16+
<i class="fa-solid fa-upload"></i> Upload Report
17+
</button>
18+
<a href="{% url 'your_testreports' %}" class="btn btn-secondary ms-2">Back to List</a>
19+
</form>
20+
</div>
21+
{% endblock %}

testreports/templates/testreports/your-testreports.html

Lines changed: 68 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,115 +3,78 @@
33
{% block title %}Dashboard - Cleo{% endblock %}
44
{% block content %}
55

6-
<header class="page-header page-header-compact page-header-light border-bottom bg-white mb-4">
7-
<div class="container-xl px-4">
8-
<div class="page-header-content">
9-
<div class="row align-items-center justify-content-between pt-3">
10-
<div class="col-auto mb-3 mt-3">
11-
<h1 class="page-header-title">
12-
<div class="page-header-icon"><i class="fa-regular fa-file-pdf"></i></div>
13-
Dina Testrapporter
14-
</h1>
15-
</div>
16-
<div class="col-12 col-xl-auto mb-3 gap-5">
17-
<a class="btn btn-sm btn-light text-primary" href="{% url 'upload_testreport' %}">
18-
<i class="fa-regular fa-file-pdf"></i>
19-
Ladda upp testrapport
20-
</a>
21-
</div>
6+
<header class="page-header page-header-compact page-header-light border-bottom bg-white mb-4">
7+
<div class="container-xl px-4">
8+
<div class="page-header-content">
9+
<div class="row align-items-center justify-content-between pt-3">
10+
<div class="col-auto mb-3 mt-3">
11+
<h1 class="page-header-title">
12+
<div class="page-header-icon"><i class="fa-regular fa-file-pdf"></i></div>
13+
Dina Testrapporter
14+
</h1>
15+
</div>
16+
<div class="col-12 col-xl-auto mb-3 gap-5">
17+
<a class="btn btn-sm btn-light text-primary" href="{% url 'upload_testreport' %}">
18+
<i class="fa-regular fa-file-pdf"></i>
19+
Ladda upp testrapport
20+
</a>
2221
</div>
2322
</div>
2423
</div>
25-
</header>
26-
<!-- Main page content-->
27-
<div class="container-xl px-4 mt-4">
28-
<div class="row">
29-
<div class="container-fluid px-4">
30-
<div>
31-
<div class="card-body">
32-
<table id="datatablesSimple">
33-
<thead>
34-
<tr>
35-
<th>Namn</th>
36-
<th>Titel</th>
37-
<th>E-post</th>
38-
<th>Telefon</th>
39-
<th>Top Skills</th>
40-
<th>Tillagd</th>
41-
<th></th>
42-
</tr>
43-
</thead>
44-
<tfoot>
45-
<tr>
46-
<th>Name</th>
47-
<th>Title</th>
48-
<th>Email</th>
49-
<th>Phone</th>
50-
<th>Top Skills</th>
51-
<th>Added date</th>
52-
<th>Actions</th>
53-
</tr>
54-
</tfoot>
55-
<tbody>
56-
{% for candidate in candidates %}
57-
<tr>
58-
<td>
59-
<div class="d-flex align-items-center">
60-
{% if candidate.slug %}
61-
<a href="{% url 'candidate_detail' candidate.slug %}" class="text-dark fw-bold text-decoration-none">
62-
{{ candidate.first_name }} {{ candidate.last_name }}
63-
</a>
64-
{% else %}
65-
<span class="text-muted">{{ candidate.first_name }} {{ candidate.last_name }} (No slug)</span>
66-
{% endif %}
67-
</div>
68-
</td>
69-
<td>
70-
{% if candidate.title %}
71-
{{ candidate.title }}
72-
{% else %}
73-
<span class="text-muted fst-italic">Ingen titel</span>
74-
{% endif %}
75-
</td>
76-
<td>{{ candidate.email }}</td>
77-
<td>{{ candidate.phone_number }}</td> <!-- Eller visa nåt från modellen om du har det -->
78-
<td>
79-
{% for skill in candidate.top_skills %}
80-
<span class="badge bg-secondary text-white">{{ skill }}</span>
81-
{% empty %}
82-
<span class="text-muted">No skills</span>
83-
{% endfor %}
84-
</td>
85-
<td>{{ candidate.created_on|date:"d M Y" }}</td>
86-
<td>
87-
{% if candidate.slug %}
88-
<a class="btn btn-datatable btn-icon btn-transparent-dark me-2" href="{% url 'edit_candidate' candidate.slug %}">
89-
<i data-feather="edit"></i>
90-
</a>
91-
{% endif %}
92-
{% if candidate.slug %}
93-
<a
94-
class="btn btn-datatable btn-icon btn-transparent-dark open-delete-modal"
95-
href="#"
96-
data-bs-toggle="modal"
97-
data-bs-target="#deleteModal"
98-
data-name="{{ candidate.first_name }} {{ candidate.last_name }}"
99-
data-url="{% url 'delete_candidate' candidate.slug %}">
100-
<i data-feather="trash-2"></i>
101-
</a>
102-
{% endif %}
103-
</td>
104-
</tr>
105-
{% empty %}
106-
<tr>
107-
<td colspan="6">You have no candidates yet.</td>
108-
</tr>
109-
{% endfor %}
110-
</tbody>
111-
</table>
112-
</div>
113-
</div>
24+
</div>
25+
</header>
26+
27+
<!-- Main page content-->
28+
<div class="container-xl px-4 mt-4">
29+
<div class="row">
30+
<div class="container-fluid px-4">
31+
<div class="card-body">
32+
<table id="datatablesSimple">
33+
<thead>
34+
<tr>
35+
<th>Filnamn</th>
36+
<th>Datum</th>
37+
<th>AI-sammanfattning</th>
38+
<th>Visa</th>
39+
</tr>
40+
</thead>
41+
<tfoot>
42+
<tr>
43+
<th>File name</th>
44+
<th>Date</th>
45+
<th>Summary</th>
46+
<th>Actions</th>
47+
</tr>
48+
</tfoot>
49+
<tbody>
50+
{% for report in reports %}
51+
<tr>
52+
<td>{{ report.uploaded_file.name|truncatechars:40 }}</td>
53+
<td>{{ report.created_on|date:"Y-m-d H:i" }}</td>
54+
<td>
55+
{% if report.summary %}
56+
57+
{% else %}
58+
<span class="text-muted">Ej genererad</span>
59+
{% endif %}
60+
</td>
61+
<td>
62+
<a class="btn btn-sm btn-outline-secondary"
63+
href="{% url 'testreport_detail' report.id %}">
64+
Öppna
65+
</a>
66+
</td>
67+
</tr>
68+
{% empty %}
69+
<tr>
70+
<td colspan="4">Inga testrapporter har laddats upp ännu.</td>
71+
</tr>
72+
{% endfor %}
73+
</tbody>
74+
</table>
11475
</div>
11576
</div>
11677
</div>
78+
</div>
79+
11780
{% endblock %}

0 commit comments

Comments
 (0)