Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kaplancloudapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ def get_absolute_url(self):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.status == 1:
NewProjectReportThread(self).run()
NewProjectReportThread(self).start()


class Segment(models.Model):
Expand Down
56 changes: 51 additions & 5 deletions kaplancloudapp/static/project.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
const REPORT_STATUS_LABELS = {
0: 'Blank',
1: 'Ready for Processing',
2: 'Processing',
3: 'Complete',
};

window.onload = function() {
const assignLinguistForm = document.getElementById('assign-linguist-form').children[0];
const checkboxMain = document.getElementById('checkbox-main');
const contextMenu = document.getElementById('context-menu');

let filesList = [];

document.querySelectorAll('#reports-body tr').forEach(row => {
const status = parseInt(row.dataset.status, 10);
if (status < 3) {
const uuid = row.id.replace(/^report-/, '');
checkReport(uuid);
}
});

checkboxMain.onclick = function() {
[...document.getElementsByClassName('checkbox')].forEach((item, i) => {
item.checked = checkboxMain.checked;
Expand Down Expand Up @@ -139,13 +154,47 @@ function analyzeFiles(fileUuids)
)
.then(response => response.json())
.then(data => {
addReportRow(data['uuid']);
checkReport(data['uuid']);
})
.catch(error => {
console.error(error);
})
}

function addReportRow(reportUuid)
{
const tbody = document.getElementById('reports-body');
const row = document.createElement('tr');
row.id = 'report-' + reportUuid;
row.dataset.status = '1';
row.className = 'hover:bg-base-200/30 transition-colors';
const href = '/report/' + reportUuid;
row.innerHTML = `
<td><a href="${href}" target="_blank" class="link link-hover">Just now</a></td>
<td><span class="badge badge-sm badge-ghost report-status">${REPORT_STATUS_LABELS[1]}</span></td>
<td>
<a href="${href}" target="_blank" class="btn btn-ghost btn-xs btn-square" aria-label="Open">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
</svg>
</a>
</td>
`;
tbody.prepend(row);
}

function setReportStatus(reportUuid, status)
{
const row = document.getElementById('report-' + reportUuid);
if (!row) return;
row.dataset.status = String(status);
const badge = row.querySelector('.report-status');
if (badge && REPORT_STATUS_LABELS[status] !== undefined) {
badge.textContent = REPORT_STATUS_LABELS[status];
}
}

function checkReport(reportUuid)
{
var url = new URL(window.location.origin + '/report/' + reportUuid);
Expand All @@ -156,14 +205,11 @@ function checkReport(reportUuid)
.then(response => response.json())
.then(data =>
{
if (data['status'] == 1 || data['status'] == 2)
setReportStatus(reportUuid, data['status']);
if (data['status'] < 3)
{
setTimeout(checkReport, 5000, reportUuid);
}
else
{
document.getElementById('report-toast').classList.add('show')
}
}
)
}
Expand Down
51 changes: 30 additions & 21 deletions kaplancloudapp/templates/project.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
.modal-overlay { display: none; position: fixed; inset: 0; z-index: 50; align-items: center; justify-content: center; background: rgb(0 0 0 / 0.5); }
.modal-overlay.show { display: flex; }
#context-menu { display: none; }
#report-toast { display: none; }
#report-toast.show { display: flex; }
</style>
{% endblock %}

Expand Down Expand Up @@ -121,17 +119,36 @@ <h2 class="text-base font-semibold mb-3">Reference</h2>
{# ── Reports section ── #}
<section>
<h2 class="text-base font-semibold mb-3">Reports</h2>
{% if reports %}
<ul class="space-y-1">
{% for report in reports %}
<li>
<a href="{% url 'report' report.uuid %}" target="_blank" class="link link-hover text-sm">{{ report.created_at }}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p class="text-sm text-base-content/40">No reports yet.</p>
{% endif %}
<div class="overflow-x-auto rounded-lg border border-base-300">
<table class="table table-sm">
<thead>
<tr class="bg-base-200/50">
<th scope="col">Created</th>
<th scope="col">Status</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="reports-body">
{% for report in reports %}
<tr id="report-{{ report.uuid }}" data-status="{{ report.status }}" class="hover:bg-base-200/30 transition-colors">
<td>
<a href="{% url 'report' report.uuid %}" target="_blank" class="link link-hover">{{ report.created_at }}</a>
</td>
<td>
<span class="badge badge-sm badge-ghost report-status">{{ report.get_status_display }}</span>
</td>
<td>
<a href="{% url 'report' report.uuid %}" target="_blank" class="btn btn-ghost btn-xs btn-square" aria-label="Open">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
</svg>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
</div>
</div>
Expand Down Expand Up @@ -199,13 +216,5 @@ <h3 class="text-lg font-semibold">Import Package</h3>
</div>
{% endif %}

{# ── Report toast ── #}
<div id="report-toast" class="fixed bottom-4 right-4 z-50 items-center gap-3 bg-base-100 border border-base-300 rounded-lg shadow-lg px-4 py-3 text-sm">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-success shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<p>Your report is ready. Please refresh this page to see it listed.</p>
</div>

{% csrf_token %}
{% endblock %}
12 changes: 7 additions & 5 deletions kaplancloudapp/templates/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
{% block title %}Report | {{ project.name }} | Kaplan Cloud{% endblock %}

{% block navbar_center %}
<ul class="breadcrumb text-sm">
<li><a href="{% url 'projects' %}" class="link link-hover">Projects</a></li>
<li><a href="{% url 'project' project.uuid %}" class="link link-hover">{{ project.name }}</a></li>
<li>Report — {{ report.created_at|date:"N j, Y, P" }}</li>
</ul>
<div class="text-sm breadcrumbs">
<ul>
<li><a href="{% url 'projects' %}" class="text-base-content/60 hover:text-base-content">Projects</a></li>
<li><a href="{% url 'project' project.uuid %}" class="text-base-content/60 hover:text-base-content">{{ project.name }}</a></li>
<li class="font-semibold">Report — {{ report.created_at|date:"N j, Y, P" }}</li>
</ul>
</div>
{% endblock %}

{% block div %}
Expand Down
4 changes: 2 additions & 2 deletions kaplancloudapp/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,14 +442,14 @@ def test_ordering_newest_first(self):

@patch("kaplancloudapp.models.NewProjectReportThread")
def test_save_with_status_1_triggers_thread(self, mock_thread):
mock_thread.return_value.run = MagicMock()
mock_thread.return_value.start = MagicMock()
report = ProjectReport.objects.create(
project=self.project, content={"s": "processing"}
)
report.status = 1
report.save()
mock_thread.assert_called_once()
mock_thread.return_value.run.assert_called_once()
mock_thread.return_value.start.assert_called_once()


# ===========================================================================
Expand Down
160 changes: 83 additions & 77 deletions kaplancloudapp/thread_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import regex
from django.apps import apps
from django.db import connection
from kaplan import open_bilingualfile
from kaplan.kdb import KDB
from kaplan.kxliff import KXLIFF
Expand Down Expand Up @@ -257,32 +258,20 @@ def __init__(self, projectreport_instance, **kwargs):
super(NewProjectReportThread, self).__init__(**kwargs)

def run(self):
instance = self.projectreport_instance

for project_file in instance.project_files.all():
if project_file.status == 1:
project_file.status = 2
project_file.save()

SegmentModel = apps.get_model("kaplancloudapp", "Segment")

entries = []

project_report = {}
project_total = {
"Repetitions": 0,
"100": 0, # TODO
"95": 0,
"85": 0,
"75": 0,
"50": 0,
"New": 0,
"Total": 0,
}

sm = difflib.SequenceMatcher()
for project_file in instance.project_files.all():
file_report = {
try:
instance = self.projectreport_instance

for project_file in instance.project_files.all():
if project_file.status == 1:
project_file.status = 2
project_file.save()

SegmentModel = apps.get_model("kaplancloudapp", "Segment")

entries = []

project_report = {}
project_total = {
"Repetitions": 0,
"100": 0, # TODO
"95": 0,
Expand All @@ -293,66 +282,83 @@ def run(self):
"Total": 0,
}

for segment in SegmentModel.objects.filter(file=project_file):
source_segment = etree.fromstring(
"<source>" + segment.source + "</source>"
)
source_entry, _ = KDB.segment_to_entry(source_segment)
word_count = len(source_entry.split())
char_count = len(source_entry)
sm = difflib.SequenceMatcher()
for project_file in instance.project_files.all():
file_report = {
"Repetitions": 0,
"100": 0, # TODO
"95": 0,
"85": 0,
"75": 0,
"50": 0,
"New": 0,
"Total": 0,
}

for segment in SegmentModel.objects.filter(file=project_file):
source_segment = etree.fromstring(
"<source>" + segment.source + "</source>"
)
source_entry, _ = KDB.segment_to_entry(source_segment)
word_count = len(source_entry.split())
char_count = len(source_entry)

if source_entry in entries:
file_report["Repetitions"] += word_count
# elif entry in project_tm_entries TODO
else:
sm.set_seq2(source_entry)

highest_match = 0.0
for entry in filter(
lambda x: len(x) >= char_count / 2 and len(x) <= char_count * 2,
entries,
):
sm.set_seq1(entry)
highest_match = max(sm.ratio(), highest_match)
if highest_match >= 0.95:
break

if highest_match >= 0.95:
file_report["95"] += word_count
elif highest_match >= 0.85:
file_report["85"] += word_count
elif highest_match >= 0.75:
file_report["75"] += word_count
elif highest_match >= 0.5:
file_report["50"] += word_count
if source_entry in entries:
file_report["Repetitions"] += word_count
# elif entry in project_tm_entries TODO
else:
file_report["New"] += word_count
sm.set_seq2(source_entry)

highest_match = 0.0
for entry in filter(
lambda x: (
len(x) >= char_count / 2 and len(x) <= char_count * 2
),
entries,
):
sm.set_seq1(entry)
highest_match = max(sm.ratio(), highest_match)
if highest_match >= 0.95:
break

entries.append(source_entry)
if highest_match >= 0.95:
file_report["95"] += word_count
elif highest_match >= 0.85:
file_report["85"] += word_count
elif highest_match >= 0.75:
file_report["75"] += word_count
elif highest_match >= 0.5:
file_report["50"] += word_count
else:
file_report["New"] += word_count

entries.append(source_entry)

file_report["Total"] += word_count
file_report["Total"] += word_count

project_total["Repetitions"] += file_report["Repetitions"]
project_total["100"] += file_report["100"]
project_total["95"] += file_report["95"]
project_total["85"] += file_report["85"]
project_total["75"] += file_report["75"]
project_total["50"] += file_report["50"]
project_total["New"] += file_report["New"]
project_total["Total"] += file_report["Total"]
project_total["Repetitions"] += file_report["Repetitions"]
project_total["100"] += file_report["100"]
project_total["95"] += file_report["95"]
project_total["85"] += file_report["85"]
project_total["75"] += file_report["75"]
project_total["50"] += file_report["50"]
project_total["New"] += file_report["New"]
project_total["Total"] += file_report["Total"]

project_report[project_file.name] = file_report
project_report[project_file.name] = file_report

project_report["Total"] = project_total
project_report["Total"] = project_total

instance.content = project_report
instance.status = 3
instance.save()
instance.content = project_report
instance.status = 3
instance.save()

for project_file in instance.project_files.all():
if project_file.status == 2:
project_file.status = 3
project_file.save()
for project_file in instance.project_files.all():
if project_file.status == 2:
project_file.status = 3
project_file.save()
finally:
connection.close()


class TMImportThread(threading.Thread):
Expand Down
Loading
Loading