chiark / gitweb /
list age at birth of children
[familyTree.git] / familyTree / askQuestion.py
1 #!/usr/bin/python
2
3 import sqlite3
4 import findYear
5 from string import Template
6
7 global link_Template 
8 link_Template= Template("<a href = http://www.chiark.greenend.org.uk/ucgi/~naath/$script>$text</a>")
9
10 def run_query(s,t):
11         c = make_cursor()
12         return c.execute(s,t)
13
14 def print_row(row,newLine):
15         out = ''
16         for item in row:
17                 out = out + str(item)+'|'
18         return out[:-1] + newLine
19
20 def print_query(s,t,newLine):
21         printMe = ''
22         for row in run_query(s,t):
23                 printMe = printMe + print_row(row,newLine)              
24         return printMe
25
26 def is_number(s):
27     try:
28         float(s)
29         return 1
30     except ValueError:
31         return 0
32
33 def print_tagged_query(relationship,s,t,newLine):
34         mine = ''
35         for row in run_query(s,t):
36                 mine = mine + print_tagged_name(relationship,row,newLine)
37         return mine
38
39
40 def relationship_html(ID,ID2,newLine):
41         if newLine=='<br>':
42                 relationship = common_ancestors(ID,ID2,newLine)[2]
43                 script = "ancestors.py?ID="+str(ID)+"&ID2="+str(ID2)
44                 url = link_Template.substitute(script = script,text = "Common ancestors")
45                 return relationship + ' '+url + newLine
46         else:
47                 return ''
48
49 def terr_html(terr,newLine):
50         if newLine=='<br>':
51                 return link_Template.substitute(script = "territory.py?terr="+terr, text=terr)
52         else:
53                 return terr
54 def name_html(row,html):
55         if html=='<br>':
56                 html=1
57         elif html=='\n':
58                 html=0
59
60         if row[0] == None:
61                 return row[1]
62         else:
63                 if html==1:
64                         script = "person.py?ID=" + str(row[1])
65                         name = row[0]
66                         return link_Template.substitute(script = script, text = name)
67                 else:
68                         return row[0] + "," +str(row[1])
69
70 def print_tagged_name(relationship,row,newLine):
71         if row[0]==None:
72                 out = relationship + " not yet entered: " + row[1]
73         else:
74                 if newLine == '<br>':
75                         html = 1
76                 else:
77                         html=0
78                 if relationship =='':
79                         out = name_html(row,html) + '   '
80                 else:
81                         out = relationship + ": " + name_html(row,html)
82         return out + newLine
83
84 def month_numbers(monthN):
85         if monthN == 0:
86                 month ='unknown month'
87         elif monthN == 1:
88                 month ='January'
89         elif monthN==2:
90                 month ='February'
91         elif monthN==3:
92                 month ='March'
93         elif monthN==4:
94                 month ='April'
95         elif monthN==5:
96                 month ='May'
97         elif monthN==6:
98                 month ='June'
99         elif monthN==7:
100                 month ='July'
101         elif monthN==8:
102                 month ='August'
103         elif monthN==9:
104                 month ='September'
105         elif monthN==10:
106                 month ='October'
107         elif monthN==11:
108                 month ='November'
109         elif monthN==12:
110                 month ='December'
111         else:
112                 month = 'Incorrectly entered month ' + str(monthN)
113         return month
114
115 def ordinal_numbers(number):
116         number = int(number)
117         if number % 10==1 and number/10 % 10 !=1:
118                 out = str(number) +'st'
119         elif number % 10==2 and number/10 % 10 !=1:
120                 out = str(number) +'nd'
121         elif number % 10==3 and number/10 % 10 !=1:
122                 out = str(number) +'rd'
123         else:
124                 out = str(number) +'th'
125         return out
126
127 def list_territories(newLine):
128         s = "SELECT DISTINCT territory"\
129         +" FROM territories"\
130         +" ORDER BY territory;"
131
132         out = ''
133         for row in run_query(s,()):
134                 out =out + terr_html(row[0],newLine) +newLine
135         return out
136
137 def list_people(newLine):
138         s = "SELECT name,id,bornyear"\
139         +" FROM people"\
140         +" ORDER BY bornyear;"
141
142         out = ''
143         year = 0
144         out = out + 'born in unknown year:' +newLine
145         for row in run_query(s,()):
146                 if row[2]!=0 and row[2]/100==0:
147                         out = out +newLine+ 'born in 1st century:' +newLine
148
149                 if row[2]/100!=year/100:
150                         century = row[2]/100 + 1
151                         out = out +newLine+ 'born in ' 
152
153                         out = out +ordinal_numbers(century) \
154                                 + ' century:' + newLine
155
156                 out = out + name_html(row,newLine) +newLine
157
158                 if row[2] == 0: #unknown year
159
160                         t = (row[1],) #person ID
161
162
163                         #died
164                         u = "SELECT diedyear FROM people WHERE ID = ?;"
165
166                         bornAfter = 0
167                         for r in run_query(u,t):
168                                 if r[0] !=0:
169                                         out = out + "died: "\
170                                          + str(r[0]) + newLine
171                                         bornAfter = r[0] -100
172
173                         #find children
174                         u = "Select people.bornYear from"\
175                                 +" people INNER JOIN parents"\
176                                 +" ON people.ID = parents.ID"\
177                                 +" WHERE parents.parentID = ?"\
178                                 + " ORDER BY people.bornYear;"
179                         
180                         hadChild=[]
181                         
182                         for r in run_query(u,t):
183                                 if r[0] != 0:
184                                         hadChild.append(r[0])
185                         
186                         bornBefore = 0
187                         if len(hadChild)!=0:
188                                 out = out + "had children in: "
189                                 for c in hadChild:
190                                         out = out + str(c) + ','
191                                 out = out[:-1] + newLine
192
193                                 bornBefore = hadChild[0]-12
194                                 if bornAfter==0:
195                                         bornAfter = hadChild[0]-100
196                         
197                         if bornAfter!=0:
198                                 if bornBefore == 0: 
199                                         out = out + "probably born "\
200                                                 +"after " + str(bornAfter)
201                                 else:
202                                         out = out + "probably born "\
203                                                 +"betwen " + str(bornAfter)\
204                                                 +" and " + str(bornBefore)
205                                 out = out + newLine
206
207
208                 year = row[2]
209         return out
210
211 def count_names(newLine):
212         s = "SELECT firstName, count(*)"\
213         +" FROM people"\
214         +" GROUP BY firstName"\
215         +" ORDER BY count(*) DESC;"
216
217         out = print_query(s,(),newLine)
218         
219
220         return out
221
222 def count_birth_month(newLine):
223         s = "SELECT bornMonth, count(*)"\
224                 +" FROM people"\
225                 +" GROUP BY bornMonth"\
226                 +" ORDER BY bornMonth;"
227
228         t = "SELECT * FROM people WHERE bornMonth = ?;"
229
230         out = ''
231         for row in run_query(s,()):
232                 month = month_numbers(row[0])
233                 out = out + month + ': ' + str(row[1]) + newLine
234
235                 if row[0]>12:
236                         u = (row[0],)
237                         out =  out +print_query(t,u,newLine)
238                 
239         return out
240
241 def count_death_month(newLine):
242         s = "SELECT diedMonth, count(*)"\
243                 +" FROM people"\
244                 +" GROUP BY diedMonth"\
245                 +" ORDER BY diedMonth;"
246
247         t = "SELECT * FROM people WHERE diedMonth = ?;"
248
249         out = ''
250         for row in run_query(s,()):
251                 month = month_numbers(row[0])
252                 out = out + month + ': ' + str(row[1]) + newLine
253
254                 if row[0]>12:
255                         u = (row[0],)
256                         out =  out +print_query(t,u,newLine)
257
258         return out
259
260 def count_age_at_child(newLine):
261
262         s = "select p1.bornYear - p2.bornYear as age, count(*)"\
263                 +" FROM"\
264                 +" parents INNER JOIN people p1"\
265                 +" ON parents.ID = p1.ID"\
266                 +" INNER JOIN people p2"\
267                 +" ON parents.parentID = p2.ID"\
268                 +" WHERE p1.bornYear <> 0 and p2.bornYear<>0"\
269                 +" GROUP BY age;"
270
271         out = print_query(s,(),newLine)
272         return out
273
274 def all_ancestors(personID,newLine):
275         #find parents
276         s = "SELECT people.Name,parents.parentID FROM"\
277                 +" parents LEFT JOIN people"\
278                 +" ON parents.parentID = people.ID"\
279                 +" WHERE parents.ID = ?"\
280                 +" AND parents.parentID <> '.';"
281
282
283         ancestors = [personID]
284         allAncestors = [personID]
285         trackLevel = [0]
286         level = 0
287
288         t = "SELECT name,id FROM people WHERE id==?"
289         id = (personID,)
290
291         out = "Ancestors of "
292         for row in run_query(t,id):
293                 out = out + name_html(row,newLine)+newLine
294
295         while len(ancestors)>0:
296                 level = level+1
297                 newA =[]
298                 thisout = newLine + parent_level(level,'parent') +':' + newLine
299                 for ancestor in ancestors:
300                         id = (ancestor,)
301                         for row in run_query(s,id):
302                                 thisout = thisout + name_html(row,newLine)+newLine
303                                 if row[1] not in allAncestors and is_number(row[1])!=0:
304                                         newA.append(row[1])
305                                         allAncestors.append(row[1])
306                                         trackLevel.append(level)
307                 ancestors = newA
308                 if len(ancestors)>0:
309                         out  = out+thisout
310         
311
312         return [out, allAncestors,trackLevel]
313
314
315 def common_ancestors(IDA, IDB,newLine):
316         out = 'Common ancestors of:' + newLine
317
318         s = "SELECT name,id FROM people WHERE id==?"
319
320
321         names=[]
322         for id in (IDA,IDB):
323                 t = (id,)
324                 for row in run_query(s,t):
325                         out = out + name_html(row,newLine)+newLine
326                         names.append(row[0])
327                 if id==IDA:
328                         out = out + 'and'
329                 out = out + newLine
330
331         if len(names)!=2:
332                 related = 'No details held on one party'
333                 out = out + related
334                 return [out,[],related]
335         
336
337         a = all_ancestors(IDA,newLine)
338         b = all_ancestors(IDB,newLine)
339         
340         ancestorsB = set(b[1])
341         ancestorsA = set(a[1])
342
343         common = ancestorsA.intersection(ancestorsB)
344         common = list(common)
345
346
347         aLevels=[]
348         bLevels=[]
349         for c in common:
350                 i = a[1].index(c)
351                 aLevels.append(a[2][i])
352                 i = b[1].index(c)
353                 bLevels.append(b[2][i])
354
355         s = "SELECT Name, ID, bornyear"\
356         +" FROM people"\
357         +" WHERE ID IN ("
358         for i in range(len(common)):
359                 s = s+ "?,"
360         if len(common)>0:
361                 s = s[:-1]
362
363
364         s = s+") ORDER BY bornyear;"
365
366
367         if len(common)==0:
368                 related = names[0]+' and '+names[1]+' are not related'
369                 out = out + newLine + related
370                 return [out, common,related]
371
372
373         out = out + print_tagged_query('',s,common,newLine)
374
375         indexA=[]
376         indexB=[]
377
378         for i in range(len(common)):
379                 if aLevels[i] == min(aLevels):
380                         indexA.append(i)
381                 if bLevels[i] == min(bLevels):
382                         indexB.append(i)
383         
384
385
386         s = "SELECT name, id"\
387         +" FROM people"\
388         +" WHERE id=?"
389
390         out  = out + newLine + 'Most Recent Common Ancestors:' + newLine
391         for a in indexA:
392                 t = (common[a],)
393                 out = out + print_tagged_query('',s,t,newLine)
394                 if a!=indexA[-1]:
395                         out = out + 'and' + newLine
396
397         out = out + parent_level(aLevels[indexA[0]],'parent')
398         if len(indexA) >1:
399                 out = out + 's'
400
401         out = out + ' of ' + name_html([names[0],IDA],newLine)+newLine
402
403         out = out + newLine
404         for b in indexB:
405                 t = (common[b],)
406                 out = out + print_tagged_query('',s,t,newLine)
407                 if b!=indexB[-1]:
408                         out = out + 'and' + newLine
409
410         out = out + parent_level(bLevels[indexB[0]],'parent')
411         if len(indexB)>1:
412                 out = out + 's'
413         out = out + ' of ' + name_html([names[1],IDB],newLine)+newLine
414
415
416         al = aLevels[indexA[0]]
417         bl = bLevels[indexB[0]]
418
419         related = relationship(al,bl,names)
420         out = out+newLine + related
421
422         return [out,common,related]
423
424 def relationship(level1, level2,names):
425
426         if level1==0 and level2==0:
427                 return names[0] + ' is ' +names[1]
428         if level1==0:
429                 return names[0] + ' is ' + names[1] + '\'s '+ parent_level(level2,'parent')
430         if level2==0:
431                 return names[1] + ' is ' + names[0] + '\'s '+ parent_level(level1,'parent')
432
433
434         if level1>=level2:
435                 remove = level1-level2
436                 cousinNum = level2-1
437         else:
438                 remove = level2-level1
439                 cousinNum = level1-1
440
441         if cousinNum==0:
442                 uaLevel =  parent_level(remove,'uncle or aunt')
443                 if level1<= level2:
444                         return names[0] + ' is ' + names[1] + '\'s ' + uaLevel
445
446                 if level2<level1:
447                         return names[1] + ' is ' + names[0] + '\'s ' + uaLevel
448
449         c=ordinal_numbers(cousinNum)
450         if remove == 1:
451                 rem = 'once'
452         elif remove ==2:
453                 rem = 'twice'
454         else:
455                 rem = str(remove) + ' times'            
456
457         r = names[0] +' and '+names[1] +' are ' + c + ' cousins '
458         if remove !=0:
459                 r = r+ rem + ' removed'
460
461         return r
462
463 def parent_level(level,type):
464         if level == 0:
465                 if type=='parent':
466                         return 'self'
467                 else:           
468                         return 'sibling'
469         out = type
470         if level ==1:
471                 return out
472         if type =='parent':
473                 out = 'grand '+out
474         else:
475                 out = 'great '+out
476         if level ==2:
477                 return out
478         for i in range(2,level):
479                 out = 'great '+out
480         return out
481
482 def rulers_of(aTerritory,newLine):
483
484         tq = "SELECT name, people.ID, startyear,stopyear,territory"\
485                 +" FROM territories INNER JOIN people"\
486                 +" ON people.ID = territories.ID"\
487                 +" WHERE territory LIKE ?"\
488                 +" ORDER BY territory,startyear,stopyear;"
489
490
491
492         thisT  = ''
493         last = ''
494         out = ''
495         for row in run_query(tq,(aTerritory+'%',)):
496                 if row[4]!=last and last!='':
497                         out  = out + 'Rulers of '+terr_html(last,newLine) +':'+ newLine +thisT +newLine
498                         thisT = ''
499
500                 thisT = thisT +name_html(row,newLine)
501                 thisT = thisT +' from ' + str(row[2])+' to '+str(row[3]) + newLine
502                 last = row[4]
503
504         out  = out + 'Rulers of '+terr_html(row[4],newLine) +':'+ newLine +thisT
505
506         return out      
507
508 def person_info(personID,newLine):
509         t = (personID,)
510
511         output = '';
512         
513         #Id, Name, Dates, Style, Style-Dates
514         s = "SELECT * FROM people WHERE ID = ?"
515         for row in run_query(s,t):
516                 output = output + 'ID: '+str(row[0]) +newLine
517                 output = output + print_tagged_name('Name',[row[1], row[0]],newLine) +newLine
518                 output = output + 'Born: '+row[3] + newLine
519                 bornYear = row[4]
520                 output = output + 'Died: '+row[5] + newLine
521
522         s = "SELECT * FROM styles WHERE ID = ?"
523         for row in run_query(s,t):
524                 output = output +newLine+ 'Style: '+row[1] + newLine
525
526                 output = output + 'Territories:' + newLine
527
528                 u = "SELECT * FROM territories"\
529                 +"  WHERE ID =? AND startYear =? AND stopYear=?"
530                 v=(personID,row[3],row[5])
531
532                 any = 0
533                 for r in run_query(u,v):
534                         output = output + terr_html(r[1],newLine) +','
535                         any = 1
536                 if any ==1:
537                         output = output[:-1] + newLine
538
539                 output = output +  'From: '+row[2] + newLine
540                 output = output +  'To: '+row[4] + newLine
541
542         s = "SELECT people.Name,consort "\
543                 +"FROM consorts LEFT JOIN people"\
544                 +" ON people.ID = consorts.consort"\
545                 +" WHERE consorts.ID = ?"
546         for row in run_query(s,t):
547                 output = output + print_tagged_name('Consort',row,newLine)
548
549         output = output + newLine
550         #find parents
551         s = "SELECT people.Name,parents.parentID FROM"\
552                 +" parents LEFT JOIN people"\
553                 +" ON parents.parentID = people.ID"\
554                 +" WHERE parents.ID = ?"
555         for row in run_query(s,t):
556                 output = output + print_tagged_name('Parent',row,newLine)
557
558         #find spouses
559         s = "SELECT people.NAME, marriages.IDb from"\
560                 +" marriages LEFT JOIN people"\
561                 +" ON people.ID = marriages.IDb"\
562                 +" WHERE marriages.IDa = ?"
563         for row in run_query(s,t):
564                 output = output + newLine
565                 output = output + print_tagged_name('Spouse',row,newLine)
566                 output = output + relationship_html(personID,row[1],newLine)
567
568         s = "SELECT people.NAME, marriages.IDa from"\
569                 +" marriages LEFT JOIN people"\
570                 +" ON people.ID = marriages.IDa"\
571                 +" WHERE marriages.IDb = ?"
572         for row in run_query(s,t):    
573                 output = output + newLine
574                 output = output + print_tagged_name('Spouse',row,newLine)
575                 output = output + relationship_html(personID,row[1],newLine)
576
577         output = output + newLine
578
579         #find children
580         s = "Select people.NAME, people.ID ,people.bornYear"\
581                 +" FROM people INNER JOIN parents"\
582                 +" ON people.ID = parents.ID"\
583                 +" WHERE parents.parentID = ?"\
584                 +" ORDER BY people.bornYear;"
585
586         for row in run_query(s,t):
587                 output = output  + print_tagged_name('Child',row,newLine)
588
589                  #find children's other parent
590                 u = "Select people.NAME, parents.parentID FROM"\
591                 +" parents LEFT JOIN people"\
592                 +" ON people.ID = parents.parentID"\
593                 +" WHERE parents.ID = ? AND parents.parentID <> ?"
594
595                 ids = (row[1],t[0])
596
597                 for r in run_query(u,ids):
598                         output = output + print_tagged_name('With',r,newLine)
599
600                 #age when child born
601                 age = row[2]-bornYear
602                 output = output[:-4] + " at the age of "+str(age) + newLine
603
604         output = output + newLine
605
606         return output
607
608 def connect():
609         global conn
610         conn = sqlite3.connect('/home/naath/familyTreeProject/familyTree/tree.db')
611         return conn
612
613 def make_cursor():
614         return conn.cursor()
615         
616 def close(conn):
617         conn.close
618
619 #def main():
620
621 #       [c, conn] = connect()   
622 #
623 #       person_info(1,c)
624 #       person_info(17,c)
625 #       person_info(38,c)
626 #       person_info(90,c)
627 #
628 #       close(conn)