Skip to content

Commit 4eb1ce2

Browse files
Another stats table
1 parent c65e56a commit 4eb1ce2

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed

web/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,3 +384,12 @@ class LookupData(models.Model):
384384
version2 = models.CharField(max_length=20, default='')
385385
structure = models.CharField(max_length=50)
386386
site_visit = models.ForeignKey(SiteVisit, on_delete=models.CASCADE)
387+
388+
389+
class MissingLookup(models.Model):
390+
id = models.BigAutoField(primary_key=True)
391+
date_time = models.DateTimeField(auto_now_add=True)
392+
item_type = models.CharField(max_length=20) # 'language', 'structure', 'concept'
393+
item_value = models.CharField(max_length=100)
394+
language_context = models.CharField(max_length=50, blank=True, null=True)
395+
site_visit = models.ForeignKey(SiteVisit, on_delete=models.CASCADE)

web/templates/statistics.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,48 @@ <h5 class="mb-0"><i class="fas fa-exchange-alt"></i> Top Comparisons</h5>
136136
</div>
137137
</div>
138138

139+
<div class="row">
140+
<div class="col-12">
141+
<div class="card mb-4 shadow-sm border-danger">
142+
<div class="card-header bg-danger text-white">
143+
<h5 class="mb-0"><i class="fas fa-exclamation-triangle"></i> Most Requested Missing Items</h5>
144+
</div>
145+
<div class="card-body">
146+
<p class="text-muted small">Tracking languages, structures, or concepts that were requested but are currently missing from the site.</p>
147+
{% if missing_items %}
148+
<table class="table table-hover table-sm">
149+
<thead>
150+
<tr>
151+
<th>Item</th>
152+
<th class="text-end">Requests</th>
153+
</tr>
154+
</thead>
155+
<tbody>
156+
{% for item in missing_items %}
157+
<tr>
158+
<td>
159+
{% if item.type == 'language' %}
160+
<span class="badge bg-primary">Language</span>
161+
{% elif item.type == 'structure' %}
162+
<span class="badge bg-info">Structure</span>
163+
{% else %}
164+
<span class="badge bg-secondary">Concept</span>
165+
{% endif %}
166+
{{ item.label }}
167+
</td>
168+
<td class="text-end">{{ item.count }}</td>
169+
</tr>
170+
{% endfor %}
171+
</tbody>
172+
</table>
173+
{% else %}
174+
<p class="text-center">No missing items requested yet.</p>
175+
{% endif %}
176+
</div>
177+
</div>
178+
</div>
179+
</div>
180+
139181
<div class="row">
140182
<div class="col-md-6">
141183
<div class="card mb-4 shadow-sm">

web/views.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
LookupData,
2626
MetaInfo,
2727
MissingLanguageError,
28+
MissingLookup,
2829
MissingStructureError,
2930
SiteVisit,
3031
)
@@ -63,6 +64,16 @@ def store_lookup_info(request, visit, language1, version1, language2, version2,
6364
info.save()
6465

6566

67+
def store_missing_info(visit, item_type, item_value, language_context=None):
68+
info = MissingLookup(
69+
item_type=item_type,
70+
item_value=item_value,
71+
language_context=language_context,
72+
site_visit=visit
73+
)
74+
info.save()
75+
76+
6677
@require_http_methods(['GET'])
6778
def index(request):
6879
"""
@@ -239,6 +250,34 @@ def statistics(request):
239250
'date_time': item.date_time
240251
})
241252

253+
# Missing items statistics
254+
missing_items_counts = MissingLookup.objects.values('item_type', 'item_value', 'language_context') \
255+
.annotate(count=Count('id')).order_by('-count')[:15]
256+
257+
missing_items = []
258+
for item in missing_items_counts:
259+
label = item['item_value']
260+
if item['item_type'] == 'language':
261+
label = f"Language: {item['item_value']}"
262+
elif item['item_type'] == 'structure':
263+
try:
264+
lang_name = meta_info.language_name(item['language_context'])
265+
except (KeyError, MissingLanguageError):
266+
lang_name = item['language_context']
267+
label = f"Structure: {item['item_value']} (for {lang_name})"
268+
elif item['item_type'] == 'concept':
269+
try:
270+
lang_name = meta_info.language_name(item['language_context'])
271+
except (KeyError, MissingLanguageError):
272+
lang_name = item['language_context']
273+
label = f"Concept: {item['item_value']} (missing in {lang_name})"
274+
275+
missing_items.append({
276+
'label': label,
277+
'count': item['count'],
278+
'type': item['item_type']
279+
})
280+
242281
import json
243282
context = {
244283
'title': 'Statistics',
@@ -247,6 +286,7 @@ def statistics(request):
247286
'popular_comparisons': popular_comparisons,
248287
'popular_concept_langs': popular_concept_langs,
249288
'recent_lookups': recent_lookups,
289+
'missing_items': missing_items,
250290
'total_visits': total_visits,
251291
'total_lookups': total_lookups,
252292
'unique_comparisons_count': unique_comparisons_count,
@@ -300,6 +340,12 @@ def concepts(request):
300340
try:
301341
languages = meta_info.load_languages(language_strings, meta_structure)
302342
except MissingStructureError as missing_structure:
343+
store_missing_info(
344+
visit,
345+
'structure',
346+
missing_structure.structure.key,
347+
missing_structure.language_key
348+
)
303349
return HttpResponseNotFound(render(
304350
request,
305351
"error_missing_structure.html",
@@ -317,6 +363,7 @@ def concepts(request):
317363
}
318364
))
319365
except MissingLanguageError as missing_language:
366+
store_missing_info(visit, 'language', missing_language.key)
320367
errors.append(f"The language \"{missing_language.key}\" isn't valid. \
321368
Double-check your URL and try again.")
322369

@@ -338,7 +385,7 @@ def concepts(request):
338385

339386
for (category_key, category) in meta_structure.categories.items():
340387
concept_keys = list(category.keys())
341-
concepts_list = [concepts_data(key, name, languages, lexers) for (key, name) in category.items()]
388+
concepts_list = [concepts_data(key, name, languages, lexers, visit) for (key, name) in category.items()]
342389

343390
category_entry = {
344391
"key": category_key,
@@ -503,19 +550,25 @@ def format_comment_for_display(concept_key, lang):
503550
return lang.concept_comment(concept_key)
504551

505552

506-
def concepts_data(key, name, languages, lexers=None):
553+
def concepts_data(key, name, languages, lexers=None, visit=None):
507554
"""
508555
Generates the comparision object of a single concept
509556
510557
:param key: key of the concept
511558
:param name: name of the concept
512559
:param languages: list of languages to compare / get a reference for
513560
:param lexers: optional list of pre-fetched lexers corresponding to languages
561+
:param visit: optional SiteVisit for logging missing items
514562
:return: string with code with applied HTML formatting
515563
"""
516564
data = []
517565
for i, lang in enumerate(languages):
518566
lexer = lexers[i] if lexers else None
567+
568+
# Log if concept is not implemented
569+
if visit and not lang.concept_implemented(key):
570+
store_missing_info(visit, 'concept', key, lang.key)
571+
519572
data.append({
520573
"code": format_code_for_display(key, lang, lexer),
521574
"comment": format_comment_for_display(key, lang)
@@ -584,9 +637,16 @@ def api_reference(request, structure_key, lang, version):
584637
try:
585638
response = lang_obj.load_filled_concepts(structure_key, version)
586639
except Exception as e:
640+
# Determine if it's a language or structure issue
641+
# If Language(lang, "") failed to find versions, it might be a language issue
642+
if not lang_obj.versions():
643+
store_missing_info(visit, 'language', lang)
644+
else:
645+
store_missing_info(visit, 'structure', structure_key, lang)
587646
return error_handler_404_not_found(request, e)
588647

589648
if response is False:
649+
store_missing_info(visit, 'structure', structure_key, lang)
590650
return HttpResponseNotFound()
591651

592652
store_lookup_info(
@@ -615,9 +675,15 @@ def api_compare(request, structure_key, lang1, version1, lang2, version2):
615675
"""
616676
visit = store_url_info(request)
617677

618-
response = Language(lang1, "").load_comparison(structure_key, lang2, version2, version1)
678+
try:
679+
response = Language(lang1, "").load_comparison(structure_key, lang2, version2, version1)
680+
except Exception:
681+
# Simple logging for now
682+
store_missing_info(visit, 'structure', structure_key, f"{lang1}/{lang2}")
683+
return HttpResponseNotFound()
619684

620685
if response is False:
686+
store_missing_info(visit, 'structure', structure_key, f"{lang1}/{lang2}")
621687
return HttpResponseNotFound()
622688

623689
store_lookup_info(

0 commit comments

Comments
 (0)