summaryrefslogtreecommitdiffstats
path: root/core/views/logbooks.py
blob: 52e2d114f009fc74c2ef13aaf74e4c1f91941e2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
import datetime
import time
import os.path
import re

import django.db.models
from django.db.models import Min, Max
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.template import Context, loader
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.views.generic.list import ListView

from troggle.core.models.troggle import Expedition, Person, PersonExpedition
from troggle.core.utils import TROG
from troggle.core.models.caves import LogbookEntry, PersonTrip
from troggle.core.models.survex import SurvexBlock, Wallet
from .auth import login_required_if_public
from troggle.parsers.logbooks import LoadLogbookForExpedition
from troggle.parsers.people import GetPersonExpeditionNameLookup

import troggle.settings as settings

'''These views are for logbook items when they appear in an 'expedition' page
and for persons: their individual pages and their perseonexpedition pages.

It uses the global object TROG to hold some cached pages.
'''

todo = '''Fix the get_person_chronology() display bug.
'''

def notablepersons(request):
    def notabilitykey(person):
      return person.notability()

    persons = Person.objects.all()
    # From what I can tell, "persons" seems to be the table rows, while "pcols" is the table columns. - AC 16 Feb 09
    pcols = [ ]
    ncols = 4
    nc = int((len(persons) + ncols - 1) / ncols)
    for i in range(ncols):
        pcols.append(persons[i * nc: (i + 1) * nc])
    
    notablepersons = []
#   Needed recoding because of Django CVE-2021-45116
    for person in persons:
        if person.bisnotable():
            notablepersons.append(person)
    notablepersons.sort(key=notabilitykey, reverse=True)

    return render(request,'notablepersons.html', {'persons': persons, 'pcols':pcols, 'notablepersons':notablepersons})


def expedition(request, expeditionname):
    '''Returns a rendered page for one expedition, specified by the year e.g. '2019'.
    If page caching is enabled, it caches the dictionaries used to render the template page.
    
    '''
    if request.user.is_authenticated: 
        if "reload" in request.GET:
            this_expedition = Expedition.objects.get(year=int(expeditionname))
            # Need to delete the existing entries or we get duplication
            # Need to delete both in the Django ORM and in our own object-store.
            entries = this_expedition.logbookentry_set.all()
            for entry in entries:
                #print(f'! - delete entry: "{entry}"')
                entry.delete() 
            entries = this_expedition.logbookentry_set.all()
            LoadLogbookForExpedition(this_expedition) 
        logged_in = True
    else:
        logged_in = False
      

    ts = TROG['pagecache']['expedition']
    if settings.CACHEDPAGES:
        nexpos = len( TROG['pagecache']['expedition'])
        #print(f'! - expo {expeditionname} CACHEDPAGES {nexpos} expo pages in cache.')           
        if expeditionname in ts:
            #print('! - expo {expeditionanme} using cached page')           
            return render(request,'expedition.html', { **ts[expeditionname], 'logged_in' : logged_in })
                    
    try:
        this_expedition = Expedition.objects.get(year=int(expeditionname))
    except:
        message = f'Expedition not found - database apparently empty, you probably need to do a full re-import of all data.'
        return render(request, 'errors/generic.html', {'message': message})   

    expeditions = Expedition.objects.all()
    personexpeditiondays = [ ]
    dateditems = list(this_expedition.logbookentry_set.all()) + list(this_expedition.survexblock_set.all())
    dates = sorted(set([item.date for item in dateditems]))
    for personexpedition in this_expedition.personexpedition_set.all():
        prow = [ ]
        for date in dates:
            pcell = { "persontrips": PersonTrip.objects.filter(personexpedition=personexpedition, 
                                                                logbook_entry__date=date) }
            pcell["survexblocks"] = set(SurvexBlock.objects.filter(survexpersonrole__personexpedition=personexpedition, 
                                                                    date = date))
            prow.append(pcell)
        personexpeditiondays.append({"personexpedition":personexpedition, "personrow":prow})
        
    
    ts[expeditionname] = {'expedition': this_expedition, 'expeditions':expeditions, 
        'personexpeditiondays':personexpeditiondays, 'settings':settings, 
        'dateditems': dateditems}
    TROG['pagecache']['expedition'][expeditionname] = ts[expeditionname]
    nexpos = len( TROG['pagecache']['expedition'])
    #print(f'! - expo {expeditionname}  pre-render N expos:{nexpos}')           
    return render(request,'expedition.html', { **ts[expeditionname], 'logged_in' : logged_in } )


# def get_absolute_url(self): # seems to have come seriously adrift. This should be in a class?!
    # return ('expedition', (expedition.year))


class Expeditions_tsvListView(ListView): 
    """This uses the Django built-in shortcut mechanism 
    It defaults to use a template with name <app-label>/<model-name>_list.html.
    https://www.agiliq.com/blog/2017/12/when-and-how-use-django-listview/
    https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views
    Either a queryset variable  or set_queryset() function is used, but not needed
    if you want all the obejcts of a particaulr type in which case just set model = <object>
    """
    template_name = 'core/expeditions_tsv_list.html' # if not present then uses core/expedition_list.html 
    #queryset = Expedition.objects.all()
    #context_object_name = 'expedition'
    model = Expedition # equivalent to .objects.all() for a queryset

class Expeditions_jsonListView(ListView): 
    template_name = 'core/expeditions_json_list.html' 
    model = Expedition 


def person(request, first_name='', last_name='', ):
    try:
        this_person = Person.objects.get(first_name = first_name, last_name = last_name)
    except:
        message = f'Person not found \'{first_name} {last_name}\' - possibly Scottish? (See our <a href="/handbook/troggle/namesredesign.html">Proposal to fix this</a>)'
        return render(request, 'errors/generic.html', {'message': message})  
        
    
    return render(request,'person.html', {'person': this_person })
   


def get_person_chronology(personexpedition):
    '''Horrible bug here when there is more than one survex block per day, it duplicates the entry but gets it wrong
    Fortunately this is just the display on this page which is wroing, no bad calculations get into the database.
    
    This is just a nasty convoluted way of trying the make the template do more work than it is sensible to ask it to do.
    Rewrite more simply with the login in the python, not in Django template language (you bastard Curtis).
    '''
    res = { }
    for persontrip in personexpedition.persontrip_set.all():
        a = res.setdefault(persontrip.logbook_entry.date, { })
        a.setdefault("persontrips", [ ]).append(persontrip)

    for personrole in personexpedition.survexpersonrole_set.all():
        a = res.setdefault(personrole.survexblock.date, { })
        a.setdefault("personroles", [ ]).append(personrole.survexblock)
    
    # build up the tables
    rdates = sorted(list(res.keys()))

    res2 = [ ]
    for rdate in rdates:
        persontrips   = res[rdate].get("persontrips", [])
        personroles   = res[rdate].get("personroles", [])
        for n in range(max(len(persontrips), len(personroles) )):
            res2.append(((n == 0 and rdate or "--"), (n < len(persontrips) and persontrips[n]), (n < len(personroles) and personroles[n]) ))
            
    return res2


def personexpedition(request, first_name='',  last_name='', year=''):
    person = Person.objects.get(first_name = first_name, last_name = last_name)
    this_expedition = Expedition.objects.get(year=year)
    personexpedition = person.personexpedition_set.get(expedition=this_expedition)
    personchronology = get_person_chronology(personexpedition)
    #for pc in personchronology:
        #print(pc)
    return render(request,'personexpedition.html', {'personexpedition': personexpedition, 'personchronology':personchronology})


def logbookentry(request, date, slug):
    # start = time.time()
    trips = LogbookEntry.objects.filter(date=date) # all the trips not just this one
    this_logbookentry = trips.filter(date=date, slug=slug)
    
    if this_logbookentry:
        if len(this_logbookentry)>1:
            return render(request, 'object_list.html',{'object_list':this_logbookentry})
        else:
            wallets = set()
            allwallets = Wallet.objects.all()
            refwallets = allwallets.filter(survexblock__date=date)
            for r in refwallets:
                wallets.add(r)
           
            # Note that w.year() only works for wallets which have a valid JSON file existing
            # This is very slow with a big lag as w.date() is a computed field
            # Noticably slow with WSL2 and NTFS filesystem, even with caching as walletdate.
            jwallets = allwallets.filter(walletdate=date)
            for j in jwallets:
                wallets.add(j)
            thisexpo = this_expedition = Expedition.objects.get(year=int(date[0:4]))
            if thisexpo:
                expeditionday = thisexpo.get_expedition_day(date)
                svxothers = SurvexBlock.objects.filter(expeditionday=expeditionday)
            else:
                svxothers = None
         
            this_logbookentry=this_logbookentry[0]
            # This is the only page that uses presontrip_next and persontrip_prev
            # and it is calculated on the fly in the model 
            # duration = time.time()-start
            # print(f"--- Render after {duration:.2f} seconds")
            return render(request, 'logbookentry.html', 
                {'logbookentry': this_logbookentry, 'trips': trips, 'svxothers': svxothers, 'wallets': wallets})
    else:
        msg =(f' Logbook entry slug:"{slug}" not found in database on date:"{date}" ')
        print(msg)
        return render(request, 'errors/generic.html',{'message':msg})

def logbookSearch(request, extra):
    query_string = ''
    found_entries = None
    if ('q' in request.GET) and request.GET['q'].strip():
        query_string = request.GET['q']
    entry_query = search.get_query(query_string, ['text','title',])
    found_entries = LogbookEntry.objects.filter(entry_query)

    return render(request,'logbooksearch.html',
                          { 'query_string': query_string, 'found_entries': found_entries, })

def get_people(request, expeditionslug):
    exp = Expedition.objects.get(year = expeditionslug)
    return render(request,'options.html', {"items": [(pe.slug, pe.name) for pe in exp.personexpedition_set.all()]})

def get_logbook_entries(request, expeditionslug):
    exp = Expedition.objects.get(year = expeditionslug)
    return render(request,'options.html', {"items": [(le.slug, f"{le.date} - {le.title}") for le in exp.logbookentry_set.all()]})