big update of commit database; new CGI, better database cache; no more
html templates; better intigration with viewcvs.cgi's HTML settings git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@207 8cb11bc2-c004-0410-86c3-e597b4017df7remotes/tags/V0_8
parent
d29fac8ba9
commit
a64dc83028
289
cgi/query.cgi
289
cgi/query.cgi
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
|
@ -13,10 +13,12 @@
|
|||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# CGI script to process and display queries to CVSdb
|
||||
# query.cgi: View CVS commit database by web browser
|
||||
#
|
||||
# This script is part of the ViewCVS package. More information can be
|
||||
# found at http://viewcvs.sourceforge.net/.
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This is a teeny stub to launch the main ViewCVS app. It checks the load
|
||||
# average, then loads the (precompiled) viewcvs.py file and runs it.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@ -30,10 +32,12 @@
|
|||
#
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
HTML_TEMPLATE_DIR = None
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# Adjust sys.path to include our library directory
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
|
@ -43,272 +47,5 @@ else:
|
|||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import string
|
||||
import cgi
|
||||
import time
|
||||
|
||||
import cvsdbapi
|
||||
|
||||
|
||||
## tuple of alternating row colors
|
||||
Colors = ("#ccccee", "#ffffff")
|
||||
|
||||
|
||||
## returns a tuple-list (mod-str, string)
|
||||
def listparse_string(str):
|
||||
return_list = []
|
||||
|
||||
cmd = ""
|
||||
temp = ""
|
||||
escaped = 0
|
||||
state = "eat leading whitespace"
|
||||
|
||||
for c in str:
|
||||
|
||||
## handle escaped charactors
|
||||
if not escaped and c == "\\":
|
||||
escaped = 1
|
||||
continue
|
||||
|
||||
## strip leading white space
|
||||
if state == "eat leading whitespace":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
else:
|
||||
state = "get command or data"
|
||||
|
||||
## parse to '"' or ","
|
||||
if state == "get command or data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## the data is in quotes after the command
|
||||
elif c == "\"":
|
||||
cmd = temp
|
||||
temp = ""
|
||||
state = "get quoted data"
|
||||
continue
|
||||
|
||||
## this tells us there was no quoted data, therefore no
|
||||
## command; add the command and start over
|
||||
elif c == ",":
|
||||
## strip ending whitespace on un-quoted data
|
||||
temp = string.rstrip(temp)
|
||||
return_list.append( ("", temp) )
|
||||
temp = ""
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## parse until ending '"'
|
||||
if state == "get quoted data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## look for ending '"'
|
||||
elif c == "\"":
|
||||
return_list.append( (cmd, temp) )
|
||||
cmd = ""
|
||||
temp = ""
|
||||
state = "eat comma after quotes"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
|
||||
## parse until ","
|
||||
if state == "eat comma after quotes":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
|
||||
elif c == ",":
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
else:
|
||||
print "format error"
|
||||
sys.exit(1)
|
||||
|
||||
if cmd or temp:
|
||||
return_list.append( (cmd, temp) )
|
||||
|
||||
return return_list
|
||||
|
||||
|
||||
def decode_command(cmd):
|
||||
if cmd == "r":
|
||||
return "regex"
|
||||
elif cmd == "l":
|
||||
return "like"
|
||||
else:
|
||||
return "exact"
|
||||
|
||||
|
||||
def FormToCheckinQuery(form):
|
||||
query = cvsdbapi.CreateCheckinQuery()
|
||||
|
||||
if form.has_key("repository"):
|
||||
temp = form["repository"].value
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetRepository(str, cmd)
|
||||
|
||||
if form.has_key("branch"):
|
||||
temp = form["branch"].value
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetBranch(str, cmd)
|
||||
|
||||
if form.has_key("directory"):
|
||||
temp = form["directory"].value
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetDirectory(str, cmd)
|
||||
|
||||
if form.has_key("file"):
|
||||
temp = form["file"].value
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetFile(str, cmd)
|
||||
|
||||
if form.has_key("who"):
|
||||
temp = form["who"].value
|
||||
for cmd, str in listparse_string(temp):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetAuthor(str, cmd)
|
||||
|
||||
if form.has_key("sortby"):
|
||||
temp = form["sortby"].value
|
||||
if temp == "date":
|
||||
query.SetSortMethod("date")
|
||||
elif temp == "author":
|
||||
query.SetSortMethod("author")
|
||||
else:
|
||||
query.SetSortMethod("file")
|
||||
|
||||
if form.has_key("date"):
|
||||
temp = form["date"].value
|
||||
if temp == "hours":
|
||||
if form.has_key("hours"):
|
||||
hours = string.atoi(form["hours"].value)
|
||||
else:
|
||||
hours = 2
|
||||
query.SetFromDateHoursAgo(hours)
|
||||
elif temp == "day":
|
||||
query.SetFromDateDaysAgo(1)
|
||||
elif temp == "week":
|
||||
query.SetFromDateDaysAgo(7)
|
||||
elif temp == "month":
|
||||
query.SetFromDateDaysAgo(31)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def PrintCommitRow(commit, color):
|
||||
cTime = commit.GetTime()
|
||||
if not cTime:
|
||||
cTime = " ";
|
||||
else:
|
||||
cTime = time.strftime("%y/%m/%d %H:%M", time.localtime(cTime))
|
||||
|
||||
cAuthor = commit.GetAuthor()
|
||||
if not cAuthor:
|
||||
cAuthor = " ";
|
||||
|
||||
cFile = os.path.join(commit.GetDirectory(), commit.GetFile())
|
||||
if not cFile:
|
||||
cFile = " ";
|
||||
|
||||
cRevision = commit.GetRevision()
|
||||
if not cRevision:
|
||||
cRevision = " ";
|
||||
|
||||
cBranch = commit.GetBranch()
|
||||
if not cBranch:
|
||||
cBranch = " ";
|
||||
|
||||
cPlusMinus = '%d/%d' % (commit.GetPlusCount(), commit.GetMinusCount())
|
||||
|
||||
cDescription = commit.GetDescription()
|
||||
if not cDescription:
|
||||
cDescription = " ";
|
||||
else:
|
||||
cDescription = cgi.escape(cDescription)
|
||||
cDescription = string.replace(cDescription, '\n', '<br>')
|
||||
|
||||
print '<tr bgcolor="%s"><td align=left valign=top>%s</td>\
|
||||
<td align=left valign=top>%s</td>\
|
||||
<td align=left valign=top>%s</td>\
|
||||
<td align=left valign=top>%s</td>\
|
||||
<td align=left valign=top>%s</td>\
|
||||
<td aligh=left valign=top>%s</td>\
|
||||
<td align=left valign=top>%s</td></tr>' % (
|
||||
color, cTime, cAuthor, cFile, cRevision, cBranch,
|
||||
cPlusMinus, cDescription)
|
||||
|
||||
|
||||
def PrintCommitRows(commit_list):
|
||||
color_index = 0
|
||||
for commit in commit_list:
|
||||
PrintCommitRow(commit, Colors[color_index])
|
||||
color_index = (color_index + 1) % len(Colors)
|
||||
|
||||
|
||||
g_iColorIndex = 0
|
||||
def CommitCallback(commit):
|
||||
global g_iColorIndex
|
||||
PrintCommitRow(commit, Colors[g_iColorIndex])
|
||||
g_iColorIndex = (g_iColorIndex + 1) % len(Colors)
|
||||
|
||||
|
||||
def RunQuery(query):
|
||||
query.SetCommitCB(CommitCallback)
|
||||
db = cvsdbapi.ConnectDatabaseReadOnly()
|
||||
db.RunQuery(query)
|
||||
|
||||
|
||||
class HTMLTemplate:
|
||||
def __init__(self, filename):
|
||||
self.template = open(filename, 'r').read()
|
||||
|
||||
def Print1(self):
|
||||
index = string.find(self.template, '<!-- INSERT QUERY ROWS -->')
|
||||
print self.template[:index]
|
||||
|
||||
def Print2(self):
|
||||
index = string.find(self.template, '<!-- INSERT QUERY ROWS -->')
|
||||
print self.template[index:]
|
||||
|
||||
|
||||
def Main():
|
||||
print "Content-type: text/html\n\n"
|
||||
|
||||
template_path = os.path.join(HTML_TEMPLATE_DIR, "querytemplate.html")
|
||||
template = HTMLTemplate(template_path)
|
||||
template.Print1()
|
||||
|
||||
form = cgi.FieldStorage()
|
||||
query = FormToCheckinQuery(form)
|
||||
RunQuery(query)
|
||||
|
||||
template.Print2()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Main()
|
||||
import query
|
||||
query.run_cgi()
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
HTML_TEMPLATE_DIR = None
|
||||
|
||||
# Adjust sys.path to include our library directory
|
||||
import sys
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path[:0] = ['../lib'] # any other places to look?
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os, string
|
||||
|
||||
|
||||
def HTMLHeader():
|
||||
print "Content-type: text/html"
|
||||
print
|
||||
|
||||
|
||||
def Main():
|
||||
HTMLHeader()
|
||||
template_path = os.path.join(HTML_TEMPLATE_DIR, "queryformtemplate.html")
|
||||
print open(template_path, "r").read()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Main()
|
|
@ -1,81 +0,0 @@
|
|||
<html>
|
||||
<head><title>CVSdb Query Form</title></head>
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
<h1>CVSdb Query Form for GNOME CVS Repository</h1>
|
||||
|
||||
<form method=get action='query.cgi'>
|
||||
<table border cellpading=8 cellspacing=0>
|
||||
|
||||
<tr>
|
||||
<td align=right>Repository:</td>
|
||||
<td><input type=text name=repository size=25></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right>Branch:</td>
|
||||
<td><input type=text name=branch size=25></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right>Directory:</td>
|
||||
<td><input type=text name=directory value='' size=45></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right>File:</td>
|
||||
<td><input type=text name=file value='' size=45></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right>Who:</td>
|
||||
<td><input type=text name=who value='' size=45></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right>Sort By:</td>
|
||||
<td>
|
||||
<select name=sortby>
|
||||
<option value="date">Date</option>
|
||||
<option value="author">Author</option>
|
||||
<option value="file">File</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td align=right valign=top><br>Date:</td>
|
||||
<td colspan=2>
|
||||
<table BORDER=0 CELLSPACING=0 CELLPADDING=0>
|
||||
<tr>
|
||||
<td><input type=radio name=date checked value=hours></td>
|
||||
<td>
|
||||
In the last <input type=text name=hours value=2 size=4> hours
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type=radio name=date value=day></td>
|
||||
<td>In the last day</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type=radio name=date value=week></td>
|
||||
<td>In the last week</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type=radio name=date value=month></td>
|
||||
<td>In the last month</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type=radio name=date value=all></td>
|
||||
<td>Since the beginning of time</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2><input type=submit value='Run Query'></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
</html>
|
|
@ -1,30 +0,0 @@
|
|||
<html>
|
||||
<head><title>CVSdb Query</title></head>
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
<h1>CVSdb Query</h1>
|
||||
|
||||
<table width="100%" border=0 cellspacing=0 cellpadding=2>
|
||||
<tr bgcolor="#88ff88">
|
||||
<th align=left valign=top>Date</th>
|
||||
<th align=left valign=top>Author</th>
|
||||
<th align=left valign=top>File</th>
|
||||
<th align=left valign=top>Revision</th>
|
||||
<th align=left valign=top>Branch</th>
|
||||
<th align=left valign=top>+/-</th>
|
||||
<th align=left valign=top>Description</th>
|
||||
</tr>
|
||||
<!-- INSERT QUERY ROWS -->
|
||||
<tr bgcolor="#88ff88">
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
<th align=left valign=top> </th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
160
lib/commit.py
160
lib/commit.py
|
@ -1,160 +0,0 @@
|
|||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
## the Commit class holds data on one commit, the representation is as
|
||||
## close as possible to how it should be committed and retrieved to the
|
||||
## database engine
|
||||
|
||||
class Commit:
|
||||
|
||||
## static constants for type of commit
|
||||
CHANGE = 0
|
||||
ADD = 1
|
||||
REMOVE = 2
|
||||
|
||||
def __init__(self):
|
||||
self.__directory = ''
|
||||
self.__file = ''
|
||||
self.__repository = ''
|
||||
self.__revision = ''
|
||||
self.__author = ''
|
||||
self.__branch = ''
|
||||
self.__pluscount = ''
|
||||
self.__minuscount = ''
|
||||
self.__description = ''
|
||||
self.__gmt_time = 0.0
|
||||
self.__type = Commit.CHANGE
|
||||
|
||||
def SetRepository(self, repository):
|
||||
## clean up repository path; make sure it doesn't end with a
|
||||
## path seperator
|
||||
while repository[-1] == os.sep:
|
||||
repository = repository[:-1]
|
||||
|
||||
self.__repository = repository
|
||||
|
||||
def GetRepository(self):
|
||||
return self.__repository
|
||||
|
||||
def SetDirectory(self, dir):
|
||||
## clean up directory path; make sure it doesn't begin
|
||||
## or end with a path seperator
|
||||
while dir[0] == os.sep:
|
||||
dir = dir[1:]
|
||||
while dir[-1] == os.sep:
|
||||
dir = dir[:-1]
|
||||
|
||||
self.__directory = dir
|
||||
|
||||
def GetDirectory(self):
|
||||
return self.__directory
|
||||
|
||||
def SetFile(self, file):
|
||||
## clean up filename; make sure it doesn't begin
|
||||
## or end with a path seperator
|
||||
while file[0] == os.sep:
|
||||
file = file[1:]
|
||||
while file[-1] == os.sep:
|
||||
file = file[:-1]
|
||||
|
||||
self.__file = file
|
||||
|
||||
def GetFile(self):
|
||||
return self.__file
|
||||
|
||||
def SetRevision(self, revision):
|
||||
self.__revision = revision
|
||||
|
||||
def GetRevision(self):
|
||||
return self.__revision
|
||||
|
||||
def SetTime(self, gmt_time):
|
||||
self.__gmt_time = float(gmt_time)
|
||||
|
||||
def GetTime(self):
|
||||
return self.__gmt_time
|
||||
|
||||
def SetAuthor(self, author):
|
||||
self.__author = author
|
||||
|
||||
def GetAuthor(self):
|
||||
return self.__author
|
||||
|
||||
def SetBranch(self, branch):
|
||||
if not branch:
|
||||
self.__branch = ''
|
||||
else:
|
||||
self.__branch = branch
|
||||
|
||||
def GetBranch(self):
|
||||
return self.__branch
|
||||
|
||||
def SetPlusCount(self, pluscount):
|
||||
self.__pluscount = pluscount
|
||||
|
||||
def GetPlusCount(self):
|
||||
return self.__pluscount
|
||||
|
||||
def SetMinusCount(self, minuscount):
|
||||
self.__minuscount = minuscount
|
||||
|
||||
def GetMinusCount(self):
|
||||
return self.__minuscount
|
||||
|
||||
def SetDescription(self, description):
|
||||
self.__description = description
|
||||
|
||||
def GetDescription(self):
|
||||
return self.__description
|
||||
|
||||
def SetTypeChange(self):
|
||||
self.__type = Commit.CHANGE
|
||||
|
||||
def SetTypeAdd(self):
|
||||
self.__type = Commit.ADD
|
||||
|
||||
def SetTypeRemove(self):
|
||||
self.__type = Commit.REMOVE
|
||||
|
||||
def GetType(self):
|
||||
return self.__type
|
||||
|
||||
def GetTypeString(self):
|
||||
if self.__type == Commit.CHANGE:
|
||||
return 'Change'
|
||||
elif self.__type == Commit.ADD:
|
||||
return 'Add'
|
||||
elif self.__type == Commit.REMOVE:
|
||||
return 'Remove'
|
||||
|
||||
|
||||
## entrypoints
|
||||
|
||||
def CreateCommit():
|
||||
return Commit()
|
||||
|
||||
|
||||
def PrintCommit(commit):
|
||||
print os.path.join(commit.GetDirectory(), commit.GetFile()),\
|
||||
commit.GetRevision(),\
|
||||
commit.GetAuthor()
|
||||
|
||||
if commit.GetBranch():
|
||||
print commit.GetBranch()
|
||||
print commit.GetDescription()
|
||||
print
|
||||
|
|
@ -0,0 +1,746 @@
|
|||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
|
||||
import config
|
||||
import dbi
|
||||
import rlog
|
||||
|
||||
## load configuration file, the data is used globally here
|
||||
cfg = config.Config()
|
||||
cfg.set_defaults()
|
||||
cfg.load_config(CONF_PATHNAME)
|
||||
|
||||
## error
|
||||
error = "cvsdb error"
|
||||
|
||||
## cached (active) database connections
|
||||
gCheckinDatabase = None
|
||||
gCheckinDatabaseReadOnly = None
|
||||
|
||||
|
||||
## CheckinDatabase provides all interfaces needed to the SQL database
|
||||
## back-end; it needs to be subclassed, and have its "Connect" method
|
||||
## defined to actually be complete; it should run well off of any DBI 2.0
|
||||
## complient database interface
|
||||
|
||||
class CheckinDatabase:
|
||||
def __init__(self, host, user, passwd, database):
|
||||
self._host = host
|
||||
self._user = user
|
||||
self._passwd = passwd
|
||||
self._database = database
|
||||
|
||||
## database lookup caches
|
||||
self._get_cache = {}
|
||||
self._get_id_cache = {}
|
||||
self._desc_id_cache = {}
|
||||
|
||||
def Connect(self):
|
||||
self.db = dbi.connect(
|
||||
self._host, self._user, self._passwd, self._database)
|
||||
|
||||
def sql_get_id(self, table, column, value, auto_set):
|
||||
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
|
||||
sql_args = (value, )
|
||||
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
try:
|
||||
(id, ) = cursor.fetchone()
|
||||
except TypeError:
|
||||
if not auto_set:
|
||||
return None
|
||||
else:
|
||||
return str(int(id))
|
||||
|
||||
## insert the new identifier
|
||||
sql = "INSERT INTO %s(%s) VALUES(%%s)" % (table, column)
|
||||
sql_args = (value, )
|
||||
cursor.execute(sql, sql_args)
|
||||
|
||||
return self.sql_get_id(table, column, value, 0)
|
||||
|
||||
def get_id(self, table, column, value, auto_set):
|
||||
## attempt to retrieve from cache
|
||||
try:
|
||||
return self._get_id_cache[table][column][value]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
id = self.sql_get_id(table, column, value, auto_set)
|
||||
if id == None:
|
||||
return None
|
||||
|
||||
## add to cache
|
||||
try:
|
||||
temp = self._get_id_cache[table]
|
||||
except KeyError:
|
||||
temp = self._get_id_cache[table] = {}
|
||||
|
||||
try:
|
||||
temp2 = temp[column]
|
||||
except KeyError:
|
||||
temp2 = temp[column] = {}
|
||||
|
||||
temp2[value] = id
|
||||
return id
|
||||
|
||||
def sql_get(self, table, column, id):
|
||||
sql = "SELECT %s FROM %s WHERE id=%%s" % (column, table)
|
||||
sql_args = (id, )
|
||||
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
try:
|
||||
(value, ) = cursor.fetchone()
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
return value
|
||||
|
||||
def get(self, table, column, id):
|
||||
## attempt to retrieve from cache
|
||||
try:
|
||||
return self._get_cache[table][column][id]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
value = self.sql_get(table, column, id)
|
||||
if value == None:
|
||||
return None
|
||||
|
||||
## add to cache
|
||||
try:
|
||||
temp = self._get_cache[table]
|
||||
except KeyError:
|
||||
temp = self._get_cache[table] = {}
|
||||
|
||||
try:
|
||||
temp2 = temp[column]
|
||||
except KeyError:
|
||||
temp2 = temp[column] = {}
|
||||
|
||||
temp2[id] = value
|
||||
return value
|
||||
|
||||
def get_list(self, table, field_index):
|
||||
sql = "SELECT * FROM %s" % (table)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql)
|
||||
|
||||
list = []
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if row == None:
|
||||
break
|
||||
list.append(row[field_index])
|
||||
|
||||
return list
|
||||
|
||||
def GetBranchID(self, branch, auto_set = 1):
|
||||
return self.get_id("branches", "branch", branch, auto_set)
|
||||
|
||||
def GetBranch(self, id):
|
||||
return self.get("branches", "branch", id)
|
||||
|
||||
def GetDirectoryID(self, dir, auto_set = 1):
|
||||
return self.get_id("dirs", "dir", dir, auto_set)
|
||||
|
||||
def GetDirectory(self, id):
|
||||
return self.get("dirs", "dir", id)
|
||||
|
||||
def GetFileID(self, file, auto_set = 1):
|
||||
return self.get_id("files", "file", file, auto_set)
|
||||
|
||||
def GetFile(self, id):
|
||||
return self.get("files", "file", id)
|
||||
|
||||
def GetAuthorID(self, author, auto_set = 1):
|
||||
return self.get_id("people", "who", author, auto_set)
|
||||
|
||||
def GetAuthor(self, id):
|
||||
return self.get("people", "who", id)
|
||||
|
||||
def GetRepositoryID(self, repository, auto_set = 1):
|
||||
return self.get_id("repositories", "repository", repository, auto_set)
|
||||
|
||||
def GetRepository(self, id):
|
||||
return self.get("repositories", "repository", id)
|
||||
|
||||
def SQLGetDescriptionID(self, description, auto_set = 1):
|
||||
## lame string hash, blame Netscape -JMP
|
||||
hash = len(description)
|
||||
|
||||
sql = "SELECT id FROM descs WHERE hash=%s AND description=%s"
|
||||
sql_args = (hash, description)
|
||||
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
try:
|
||||
(id, ) = cursor.fetchone()
|
||||
except TypeError:
|
||||
if not auto_set:
|
||||
return None
|
||||
else:
|
||||
return str(int(id))
|
||||
|
||||
sql = "INSERT INTO descs (hash,description) values (%s,%s)"
|
||||
sql_args = (hash, description)
|
||||
cursor.execute(sql, sql_args)
|
||||
|
||||
return self.GetDescriptionID(description, 0)
|
||||
|
||||
def GetDescriptionID(self, description, auto_set = 1):
|
||||
## attempt to retrieve from cache
|
||||
hash = len(description)
|
||||
try:
|
||||
return self._desc_id_cache[hash][description]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
id = self.SQLGetDescriptionID(description, auto_set)
|
||||
if id == None:
|
||||
return None
|
||||
|
||||
## add to cache
|
||||
try:
|
||||
temp = self._desc_id_cache[hash]
|
||||
except KeyError:
|
||||
temp = self._desc_id_cache[hash] = {}
|
||||
|
||||
temp[description] = id
|
||||
return id
|
||||
|
||||
def GetDescription(self, id):
|
||||
return self.get("descs", "description", id)
|
||||
|
||||
def GetRepositoryList(self):
|
||||
return self.get_list("repositories", 1)
|
||||
|
||||
def GetBranchList(self):
|
||||
return self.get_list("branches", 1)
|
||||
|
||||
def GetAuthorList(self):
|
||||
return self.get_list("people", 1)
|
||||
|
||||
def AddCommitList(self, commit_list):
|
||||
for commit in commit_list:
|
||||
self.AddCommit(commit)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
## MORE TIME HELL: the MySQLdb module doesn't construct times
|
||||
## correctly when created with TimestampFromTicks -- it doesn't
|
||||
## account for daylight savings time, so we use Python's time
|
||||
## module to do the conversion
|
||||
temp = time.localtime(commit.GetTime())
|
||||
ci_when = dbi.Timestamp(
|
||||
temp[0], temp[1], temp[2], temp[3], temp[4], temp[5])
|
||||
|
||||
ci_type = commit.GetTypeString()
|
||||
who_id = self.GetAuthorID(commit.GetAuthor())
|
||||
repository_id = self.GetRepositoryID(commit.GetRepository())
|
||||
directory_id = self.GetDirectoryID(commit.GetDirectory())
|
||||
file_id = self.GetFileID(commit.GetFile())
|
||||
revision = commit.GetRevision()
|
||||
sticky_tag = "NULL"
|
||||
branch_id = self.GetBranchID(commit.GetBranch())
|
||||
plus_count = commit.GetPlusCount()
|
||||
minus_count = commit.GetMinusCount()
|
||||
description_id = self.GetDescriptionID(commit.GetDescription())
|
||||
|
||||
sql = "REPLACE INTO checkins"\
|
||||
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
|
||||
" stickytag,branchid,addedlines,removedlines,descid)"\
|
||||
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
|
||||
sql_args = (ci_type, ci_when, who_id, repository_id,
|
||||
directory_id, file_id, revision, sticky_tag, branch_id,
|
||||
plus_count, minus_count, description_id)
|
||||
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
|
||||
def SQLQueryListString(self, sqlString, query_entry_list):
|
||||
sqlList = []
|
||||
|
||||
for query_entry in query_entry_list:
|
||||
## figure out the correct match type
|
||||
if query_entry.match == "exact":
|
||||
match = "="
|
||||
elif query_entry.match == "like":
|
||||
match = " LIKE "
|
||||
elif query_entry.match == "regex":
|
||||
match = " REGEXP "
|
||||
|
||||
sqlList.append(sqlString % (match, query_entry.data))
|
||||
|
||||
return "(%s)" % (string.join(sqlList, " OR "))
|
||||
|
||||
def CreateSQLQueryString(self, query):
|
||||
tableList = ["checkins"]
|
||||
condList = []
|
||||
|
||||
## XXX: this is to exclude .ver files -- RN specific hack --JMP
|
||||
tableList.append("files")
|
||||
temp = "(checkins.fileid=files.id AND files.file NOT LIKE \"%.ver\")"
|
||||
condList.append(temp)
|
||||
## XXX
|
||||
|
||||
if len(query.repository_list):
|
||||
tableList.append("repositories")
|
||||
|
||||
sql = "(checkins.repositoryid=repositories.id AND "\
|
||||
"repositories.repository%s\"%s\")"
|
||||
temp = self.SQLQueryListString(sql, query.repository_list)
|
||||
condList.append(temp)
|
||||
|
||||
if len(query.branch_list):
|
||||
tableList.append("branches")
|
||||
|
||||
sql = "(checkins.branchid=branches.id AND "\
|
||||
"branches.branch%s\"%s\")"
|
||||
temp = self.SQLQueryListString(sql, query.branch_list)
|
||||
condList.append(temp)
|
||||
|
||||
if len(query.directory_list):
|
||||
tableList.append("dirs")
|
||||
|
||||
sql = "(checkins.dirid=dirs.id AND dirs.dir%s\"%s\")"
|
||||
temp = self.SQLQueryListString(sql, query.directory_list)
|
||||
condList.append(temp)
|
||||
|
||||
if len(query.file_list):
|
||||
tableList.append("files")
|
||||
|
||||
sql = "(checkins.fileid=files.id AND files.file%s\"%s\")"
|
||||
temp = self.SQLQueryListString(sql, query.file_list)
|
||||
condList.append(temp)
|
||||
|
||||
if len(query.author_list):
|
||||
tableList.append("people")
|
||||
|
||||
sql = "(checkins.whoid=people.id AND people.who%s\"%s\")"
|
||||
temp = self.SQLQueryListString(sql, query.author_list)
|
||||
condList.append(temp)
|
||||
|
||||
if query.from_date:
|
||||
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date))
|
||||
condList.append(temp)
|
||||
|
||||
if query.to_date:
|
||||
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date))
|
||||
condList.append(temp)
|
||||
|
||||
if query.sort == "date":
|
||||
order_by = "ORDER BY checkins.ci_when DESC"
|
||||
elif query.sort == "author":
|
||||
order_by = "ORDER BY checkins.whoid"
|
||||
elif query.sort == "file":
|
||||
order_by = "ORDER BY checkins.fileid"
|
||||
|
||||
## exclude duplicates from the table list
|
||||
for table in tableList[:]:
|
||||
while tableList.count(table) > 1:
|
||||
tableList.remove(table)
|
||||
|
||||
tables = string.join(tableList, ",")
|
||||
conditions = string.join(condList, " AND ")
|
||||
|
||||
## limit the number of rows requested or we could really slam
|
||||
## a server with a large database
|
||||
limit = ""
|
||||
if cfg.cvsdb.row_limit:
|
||||
limit = "LIMIT %s" % (str(cfg.cvsdb.row_limit))
|
||||
|
||||
sql = "SELECT checkins.* FROM %s WHERE %s %s %s" % (
|
||||
tables, conditions, order_by, limit)
|
||||
|
||||
return sql
|
||||
|
||||
def RunQuery(self, query):
|
||||
sql = self.CreateSQLQueryString(query)
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql)
|
||||
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
|
||||
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
|
||||
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
|
||||
dbRemovedLines, dbDescID) = row
|
||||
|
||||
commit = CreateCommit()
|
||||
|
||||
## TIME, TIME, TIME is all fucked up; dateobject.gmticks()
|
||||
## is broken, dateobject.ticks() returns somthing like
|
||||
## GMT ticks, except it forgets about daylight savings
|
||||
## time -- we handle it ourself in the following painful way
|
||||
gmt_time = time.mktime(
|
||||
(dbCI_When.year, dbCI_When.month, dbCI_When.day,
|
||||
dbCI_When.hour, dbCI_When.minute, dbCI_When.second,
|
||||
0, 0, dbCI_When.dst))
|
||||
|
||||
commit.SetTime(gmt_time)
|
||||
|
||||
commit.SetFile(self.GetFile(dbFileID))
|
||||
commit.SetDirectory(self.GetDirectory(dbDirID))
|
||||
commit.SetRevision(dbRevision)
|
||||
commit.SetRepository(self.GetRepository(dbRepositoryID))
|
||||
commit.SetAuthor(self.GetAuthor(dbAuthorID))
|
||||
commit.SetBranch(self.GetBranch(dbBranchID))
|
||||
commit.SetPlusCount(dbAddedLines)
|
||||
commit.SetMinusCount(dbRemovedLines)
|
||||
commit.SetDescription(self.GetDescription(dbDescID))
|
||||
|
||||
query.AddCommit(commit)
|
||||
|
||||
def CheckCommit(self, commit):
|
||||
repository_id = self.GetRepositoryID(commit.GetRepository(), 0)
|
||||
if repository_id == None:
|
||||
return None
|
||||
|
||||
dir_id = self.GetDirectoryID(commit.GetDirectory(), 0)
|
||||
if dir_id == None:
|
||||
return None
|
||||
|
||||
file_id = self.GetFileID(commit.GetFile(), 0)
|
||||
if file_id == None:
|
||||
return None
|
||||
|
||||
sql = "SELECT * FROM checkins WHERE "\
|
||||
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s"
|
||||
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
|
||||
|
||||
cursor = self.db.cursor()
|
||||
cursor.execute(sql, sql_args)
|
||||
try:
|
||||
(ci_type, ci_when, who_id, repository_id,
|
||||
dir_id, file_id, revision, sticky_tag, branch_id,
|
||||
plus_count, minus_count, description_id) = cursor.fetchone()
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
return commit
|
||||
|
||||
## the Commit class holds data on one commit, the representation is as
|
||||
## close as possible to how it should be committed and retrieved to the
|
||||
## database engine
|
||||
class Commit:
|
||||
## static constants for type of commit
|
||||
CHANGE = 0
|
||||
ADD = 1
|
||||
REMOVE = 2
|
||||
|
||||
def __init__(self):
|
||||
self.__directory = ''
|
||||
self.__file = ''
|
||||
self.__repository = ''
|
||||
self.__revision = ''
|
||||
self.__author = ''
|
||||
self.__branch = ''
|
||||
self.__pluscount = ''
|
||||
self.__minuscount = ''
|
||||
self.__description = ''
|
||||
self.__gmt_time = 0.0
|
||||
self.__type = Commit.CHANGE
|
||||
|
||||
def SetRepository(self, repository):
|
||||
## clean up repository path; make sure it doesn't end with a
|
||||
## path seperator
|
||||
while len(repository) and repository[-1] == os.sep:
|
||||
repository = repository[:-1]
|
||||
|
||||
self.__repository = repository
|
||||
|
||||
def GetRepository(self):
|
||||
return self.__repository
|
||||
|
||||
def SetDirectory(self, dir):
|
||||
## clean up directory path; make sure it doesn't begin
|
||||
## or end with a path seperator
|
||||
while len(dir) and dir[0] == os.sep:
|
||||
dir = dir[1:]
|
||||
while len(dir) and dir[-1] == os.sep:
|
||||
dir = dir[:-1]
|
||||
|
||||
self.__directory = dir
|
||||
|
||||
def GetDirectory(self):
|
||||
return self.__directory
|
||||
|
||||
def SetFile(self, file):
|
||||
## clean up filename; make sure it doesn't begin
|
||||
## or end with a path seperator
|
||||
while len(file) and file[0] == os.sep:
|
||||
file = file[1:]
|
||||
while len(file) and file[-1] == os.sep:
|
||||
file = file[:-1]
|
||||
|
||||
self.__file = file
|
||||
|
||||
def GetFile(self):
|
||||
return self.__file
|
||||
|
||||
def SetRevision(self, revision):
|
||||
self.__revision = revision
|
||||
|
||||
def GetRevision(self):
|
||||
return self.__revision
|
||||
|
||||
def SetTime(self, gmt_time):
|
||||
self.__gmt_time = float(gmt_time)
|
||||
|
||||
def GetTime(self):
|
||||
return self.__gmt_time
|
||||
|
||||
def SetAuthor(self, author):
|
||||
self.__author = author
|
||||
|
||||
def GetAuthor(self):
|
||||
return self.__author
|
||||
|
||||
def SetBranch(self, branch):
|
||||
if not branch:
|
||||
self.__branch = ''
|
||||
else:
|
||||
self.__branch = branch
|
||||
|
||||
def GetBranch(self):
|
||||
return self.__branch
|
||||
|
||||
def SetPlusCount(self, pluscount):
|
||||
self.__pluscount = pluscount
|
||||
|
||||
def GetPlusCount(self):
|
||||
return self.__pluscount
|
||||
|
||||
def SetMinusCount(self, minuscount):
|
||||
self.__minuscount = minuscount
|
||||
|
||||
def GetMinusCount(self):
|
||||
return self.__minuscount
|
||||
|
||||
def SetDescription(self, description):
|
||||
self.__description = description
|
||||
|
||||
def GetDescription(self):
|
||||
return self.__description
|
||||
|
||||
def SetTypeChange(self):
|
||||
self.__type = Commit.CHANGE
|
||||
|
||||
def SetTypeAdd(self):
|
||||
self.__type = Commit.ADD
|
||||
|
||||
def SetTypeRemove(self):
|
||||
self.__type = Commit.REMOVE
|
||||
|
||||
def GetType(self):
|
||||
return self.__type
|
||||
|
||||
def GetTypeString(self):
|
||||
if self.__type == Commit.CHANGE:
|
||||
return 'Change'
|
||||
elif self.__type == Commit.ADD:
|
||||
return 'Add'
|
||||
elif self.__type == Commit.REMOVE:
|
||||
return 'Remove'
|
||||
|
||||
## QueryEntry holds data on one match-type in the SQL database
|
||||
## match is: "exact", "like", or "regex"
|
||||
class QueryEntry:
|
||||
def __init__(self, data, match):
|
||||
self.data = data
|
||||
self.match = match
|
||||
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
class CheckinDatabaseQuery:
|
||||
def __init__(self):
|
||||
## sorting
|
||||
self.sort = "date"
|
||||
|
||||
## repository to query
|
||||
self.repository_list = []
|
||||
self.branch_list = []
|
||||
self.directory_list = []
|
||||
self.file_list = []
|
||||
self.author_list = []
|
||||
|
||||
## date range in DBI 2.0 timedate objects
|
||||
self.from_date = None
|
||||
self.to_date = None
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
## commit_cb provides a callback for commits as they
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
def SetRepository(self, repository, match = "exact"):
|
||||
self.repository_list.append(QueryEntry(repository, match))
|
||||
|
||||
def SetBranch(self, branch, match = "exact"):
|
||||
self.branch_list.append(QueryEntry(branch, match))
|
||||
|
||||
def SetDirectory(self, directory, match = "exact"):
|
||||
self.directory_list.append(QueryEntry(directory, match))
|
||||
|
||||
def SetFile(self, file, match = "exact"):
|
||||
self.file_list.append(QueryEntry(file, match))
|
||||
|
||||
def SetAuthor(self, author, match = "exact"):
|
||||
self.author_list.append(QueryEntry(author, match))
|
||||
|
||||
def SetSortMethod(self, sort):
|
||||
self.sort = sort
|
||||
|
||||
def SetFromDateObject(self, ticks):
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateObject(self, ticks):
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateHoursAgo(self, hours_ago):
|
||||
ticks = time.time() - (3600 * hours_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
if self.commit_cb:
|
||||
self.commit_cb(commit)
|
||||
|
||||
def SetCommitCB(self, callback):
|
||||
self.commit_cb = callback
|
||||
|
||||
|
||||
##
|
||||
## entrypoints
|
||||
##
|
||||
def CreateCheckinDatabase(host, user, passwd, database):
|
||||
return CheckinDatabase(host, user, passwd, database)
|
||||
|
||||
def CreateCommit():
|
||||
return Commit()
|
||||
|
||||
def CreateCheckinQuery():
|
||||
return CheckinDatabaseQuery()
|
||||
|
||||
def ConnectDatabaseReadOnly():
|
||||
global gCheckinDatabaseReadOnly
|
||||
|
||||
if gCheckinDatabaseReadOnly:
|
||||
return gCheckinDatabaseReadOnly
|
||||
|
||||
gCheckinDatabaseReadOnly = CreateCheckinDatabase(
|
||||
cfg.cvsdb.host,
|
||||
cfg.cvsdb.readonly_user,
|
||||
cfg.cvsdb.readonly_passwd,
|
||||
cfg.cvsdb.database_name)
|
||||
|
||||
gCheckinDatabaseReadOnly.Connect()
|
||||
return gCheckinDatabaseReadOnly
|
||||
|
||||
def ConnectDatabase():
|
||||
global gCheckinDatabase
|
||||
|
||||
gCheckinDatabase = CreateCheckinDatabase(
|
||||
cfg.cvsdb.host,
|
||||
cfg.cvsdb.user,
|
||||
cfg.cvsdb.passwd,
|
||||
cfg.cvsdb.database_name)
|
||||
|
||||
gCheckinDatabase.Connect()
|
||||
return gCheckinDatabase
|
||||
|
||||
def RLogDataToCommitList(repository, rlog_data):
|
||||
commit_list = []
|
||||
|
||||
## the filename in rlog_data contains the entire path of the
|
||||
## repository; we strip that out here
|
||||
temp = rlog_data.filename[len(repository):]
|
||||
directory, file = os.path.split(temp)
|
||||
|
||||
for rlog_entry in rlog_data.rlog_entry_list:
|
||||
commit = CreateCommit()
|
||||
commit.SetRepository(repository)
|
||||
commit.SetDirectory(directory)
|
||||
commit.SetFile(file)
|
||||
commit.SetRevision(rlog_entry.revision)
|
||||
commit.SetAuthor(rlog_entry.author)
|
||||
commit.SetDescription(rlog_entry.description)
|
||||
commit.SetTime(rlog_entry.time)
|
||||
commit.SetPlusCount(rlog_entry.pluscount)
|
||||
commit.SetMinusCount(rlog_entry.minuscount)
|
||||
commit.SetBranch(rlog_data.LookupBranch(rlog_entry))
|
||||
|
||||
if rlog_entry.type == rlog_entry.CHANGE:
|
||||
commit.SetTypeChange()
|
||||
elif rlog_entry.type == rlog_entry.ADD:
|
||||
commit.SetTypeAdd()
|
||||
elif rlog_entry.type == rlog_entry.REMOVE:
|
||||
commit.SetTypeRemove()
|
||||
|
||||
commit_list.append(commit)
|
||||
|
||||
return commit_list
|
||||
|
||||
def GetCommitListFromRCSFile(repository, filename):
|
||||
try:
|
||||
rlog_data = rlog.GetRLogData(filename)
|
||||
except rlog.error, e:
|
||||
raise error, e
|
||||
|
||||
commit_list = RLogDataToCommitList(repository, rlog_data)
|
||||
return commit_list
|
||||
|
||||
def GetUnrecordedCommitList(repository, filename):
|
||||
commit_list = GetCommitListFromRCSFile(repository, filename)
|
||||
db = ConnectDatabase()
|
||||
|
||||
unrecorded_commit_list = []
|
||||
for commit in commit_list:
|
||||
result = db.CheckCommit(commit)
|
||||
if not result:
|
||||
unrecorded_commit_list.append(commit)
|
||||
|
||||
return unrecorded_commit_list
|
142
lib/cvsdbapi.py
142
lib/cvsdbapi.py
|
@ -1,142 +0,0 @@
|
|||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
|
||||
import database
|
||||
import query
|
||||
import rlog
|
||||
import commit
|
||||
import config
|
||||
|
||||
## error
|
||||
error = 'cvsdbapi error'
|
||||
|
||||
## database
|
||||
CreateCheckinDatabase = database.CreateCheckinDatabase
|
||||
CreateCheckinQuery = query.CreateCheckinQuery
|
||||
|
||||
## rlog
|
||||
GetRLogData = rlog.GetRLogData
|
||||
|
||||
## commit
|
||||
CreateCommit = commit.CreateCommit
|
||||
PrintCommit = commit.PrintCommit
|
||||
|
||||
## cached (active) database connections
|
||||
gCheckinDatabase = None
|
||||
gCheckinDatabaseReadOnly = None
|
||||
|
||||
## load configuration file, the data is used globally here
|
||||
cfg = config.Config()
|
||||
cfg.set_defaults()
|
||||
cfg.load_config(CONF_PATHNAME)
|
||||
|
||||
|
||||
def ConnectDatabaseReadOnly():
|
||||
global gCheckinDatabaseReadOnly
|
||||
|
||||
if gCheckinDatabaseReadOnly:
|
||||
return gCheckinDatabaseReadOnly
|
||||
|
||||
gCheckinDatabaseReadOnly = database.CreateCheckinDatabase(
|
||||
cfg.cvsdb.host,
|
||||
cfg.cvsdb.readonly_user,
|
||||
cfg.cvsdb.readonly_passwd,
|
||||
cfg.cvsdb.database_name)
|
||||
|
||||
gCheckinDatabaseReadOnly.Connect()
|
||||
return gCheckinDatabaseReadOnly
|
||||
|
||||
|
||||
def ConnectDatabase():
|
||||
global gCheckinDatabase
|
||||
|
||||
gCheckinDatabase = database.CreateCheckinDatabase(
|
||||
cfg.cvsdb.host,
|
||||
cfg.cvsdb.user,
|
||||
cfg.cvsdb.passwd,
|
||||
cfg.cvsdb.database_name)
|
||||
|
||||
gCheckinDatabase.Connect()
|
||||
return gCheckinDatabase
|
||||
|
||||
|
||||
def RLogDataToCommitList(repository, rlog_data):
|
||||
commit_list = []
|
||||
|
||||
## the filename in rlog_data contains the entire path of the
|
||||
## repository; we strip that out here
|
||||
temp = rlog_data.filename[len(repository):]
|
||||
directory, file = os.path.split(temp)
|
||||
|
||||
for rlog_entry in rlog_data.rlog_entry_list:
|
||||
commit = CreateCommit()
|
||||
commit.SetRepository(repository)
|
||||
commit.SetDirectory(directory)
|
||||
commit.SetFile(file)
|
||||
commit.SetRevision(rlog_entry.revision)
|
||||
commit.SetAuthor(rlog_entry.author)
|
||||
commit.SetDescription(rlog_entry.description)
|
||||
commit.SetTime(rlog_entry.time)
|
||||
commit.SetPlusCount(rlog_entry.pluscount)
|
||||
commit.SetMinusCount(rlog_entry.minuscount)
|
||||
commit.SetBranch(rlog_data.LookupBranch(rlog_entry))
|
||||
|
||||
if rlog_entry.type == rlog_entry.CHANGE:
|
||||
commit.SetTypeChange()
|
||||
elif rlog_entry.type == rlog_entry.ADD:
|
||||
commit.SetTypeAdd()
|
||||
elif rlog_entry.type == rlog_entry.REMOVE:
|
||||
commit.SetTypeRemove()
|
||||
|
||||
commit_list.append(commit)
|
||||
|
||||
return commit_list
|
||||
|
||||
|
||||
def GetCommitListFromRCSFile(repository, filename):
|
||||
try:
|
||||
rlog_data = GetRLogData(filename)
|
||||
except rlog.error, e:
|
||||
raise error, e
|
||||
|
||||
commit_list = RLogDataToCommitList(repository, rlog_data)
|
||||
return commit_list
|
||||
|
||||
|
||||
def GetUnrecordedCommitList(repository, filename):
|
||||
commit_list = GetCommitListFromRCSFile(repository, filename)
|
||||
db = ConnectDatabase()
|
||||
|
||||
unrecorded_commit_list = []
|
||||
for commit in commit_list:
|
||||
result = db.CheckCommit(commit)
|
||||
if not result:
|
||||
unrecorded_commit_list.append(commit)
|
||||
|
||||
return unrecorded_commit_list
|
408
lib/database.py
408
lib/database.py
|
@ -1,408 +0,0 @@
|
|||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
|
||||
import dbi
|
||||
from commit import CreateCommit, PrintCommit
|
||||
|
||||
|
||||
## base strings used in SQL querries, these should be static members
|
||||
## of the CheckinDatabase class
|
||||
|
||||
sqlBase = 'SELECT checkins.type, checkins.ci_when,checkins. whoid, checkins.repositoryid, checkins.dirid, checkins.fileid, checkins.revision, checkins.stickytag, checkins.branchid, checkins.addedlines, checkins.removedlines, checkins.descid FROM %s WHERE %s %s'
|
||||
|
||||
sqlRepository = '(checkins.repositoryid = repositories.id AND repositories.repository %s "%s")'
|
||||
sqlBranch = '(checkins.branchid = branches.id AND branches.branch %s "%s")'
|
||||
sqlDirectory = '(checkins.dirid = dirs.id AND dirs.dir %s "%s")'
|
||||
sqlFile = '(checkins.fileid = files.id AND files.file %s "%s")'
|
||||
sqlAuthor = '(checkins.whoid = people.id AND people.who %s "%s")'
|
||||
|
||||
sqlFromDate ='(checkins.ci_when >= "%s")'
|
||||
sqlToDate = '(checkins.ci_when <= "%s")'
|
||||
sqlSortByDate = 'ORDER BY checkins.ci_when DESC'
|
||||
sqlSortByAuthor = 'ORDER BY checkins.whoid'
|
||||
sqlSortByFile = 'ORDER BY checkins.fileid'
|
||||
sqlExcludeVersionFiles = '(checkins.fileid = files.id AND files.file NOT LIKE "%%.ver")'
|
||||
sqlCheckCommit = 'SELECT * FROM checkins WHERE checkins.repositoryid=%s AND checkins.dirid=%s AND checkins.fileid=%s AND checkins.revision=%s'
|
||||
|
||||
## CheckinDatabase provides all interfaces needed to the SQL database
|
||||
## back-end; it needs to be subclassed, and have its "Connect" method
|
||||
## defined to actually be complete; it should run well off of any DBI 2.0
|
||||
## complient database interface
|
||||
|
||||
class CheckinDatabase:
|
||||
def __init__(self, host, user, passwd, database):
|
||||
self.dbHost = host
|
||||
self.dbUser = user
|
||||
self.dbPasswd = passwd
|
||||
self.dbDatabase = database
|
||||
|
||||
## cache Value lookups
|
||||
self.dbGetCache = {}
|
||||
self.dbGetIDCache = {}
|
||||
self.dbDescriptionIDCache = {}
|
||||
|
||||
def Connect(self):
|
||||
self.dbConn = dbi.connect(
|
||||
self.dbHost, self.dbUser, self.dbPasswd, self.dbDatabase)
|
||||
|
||||
def SQLGetID(self, table, field, identifier, auto_set):
|
||||
sql = 'SELECT id FROM %s x WHERE x.%s="%s"' % (
|
||||
table, field, identifier)
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql)
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return row[0]
|
||||
|
||||
if not auto_set:
|
||||
return None
|
||||
|
||||
## insert the new identifier
|
||||
sql = 'INSERT INTO %s (%s) VALUES ("%s")' % (table, field, identifier)
|
||||
cursor.execute(sql)
|
||||
return self.SQLGetID(table, field, identifier, 0)
|
||||
|
||||
def GetID(self, table, field, identifier, auto_set):
|
||||
## attempt to retrieve from cache
|
||||
try:
|
||||
return self.dbGetIDCache[table][field][identifier]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
id = self.SQLGetID(table, field, identifier, auto_set)
|
||||
if not id:
|
||||
return id
|
||||
|
||||
## add to cache
|
||||
if not self.dbGetIDCache.has_key(table):
|
||||
self.dbGetIDCache[table] = {}
|
||||
if not self.dbGetIDCache[table].has_key(field):
|
||||
self.dbGetIDCache[table][field] = {}
|
||||
self.dbGetIDCache[table][field][identifier] = id
|
||||
return id
|
||||
|
||||
def SQLGet(self, table, field, id):
|
||||
sql = 'SELECT %s FROM %s x WHERE x.id="%s"' % (field, table, id)
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return row[0]
|
||||
|
||||
def Get(self, table, field, id):
|
||||
## attempt to retrieve from cache
|
||||
try:
|
||||
return self.dbGetCache[table][field][id]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
value = self.SQLGet(table, field, id)
|
||||
if not value:
|
||||
return None
|
||||
|
||||
## add to cache
|
||||
if not self.dbGetCache.has_key(table):
|
||||
self.dbGetCache[table] = {}
|
||||
if not self.dbGetCache[table].has_key(field):
|
||||
self.dbGetCache[table][field] = {}
|
||||
self.dbGetCache[table][field][id] = value
|
||||
return value
|
||||
|
||||
def GetBranchID(self, branch, auto_set = 1):
|
||||
return self.GetID('branches', 'branch', branch, auto_set)
|
||||
|
||||
def GetBranch(self, id):
|
||||
return self.Get('branches', 'branch', id)
|
||||
|
||||
def GetDirectoryID(self, dir, auto_set = 1):
|
||||
return self.GetID('dirs', 'dir', dir, auto_set)
|
||||
|
||||
def GetDirectory(self, id):
|
||||
return self.Get('dirs', 'dir', id)
|
||||
|
||||
def GetFileID(self, file, auto_set = 1):
|
||||
return self.GetID('files', 'file', file, auto_set)
|
||||
|
||||
def GetFile(self, id):
|
||||
return self.Get('files', 'file', id)
|
||||
|
||||
def GetAuthorID(self, author, auto_set = 1):
|
||||
return self.GetID('people', 'who', author, auto_set)
|
||||
|
||||
def GetAuthor(self, id):
|
||||
return self.Get('people', 'who', id)
|
||||
|
||||
def GetRepositoryID(self, repository, auto_set = 1):
|
||||
return self.GetID('repositories', 'repository', repository, auto_set)
|
||||
|
||||
def GetRepository(self, id):
|
||||
return self.Get('repositories', 'repository', id)
|
||||
|
||||
def SQLGetDescriptionID(self, description, auto_set = 1):
|
||||
## lame string hash, blame Netscape -JMP
|
||||
hash = len(description)
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(
|
||||
'SELECT id FROM descs WHERE hash=%s and description=%s',
|
||||
(hash, description))
|
||||
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return row[0]
|
||||
|
||||
if not auto_set:
|
||||
return None
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(
|
||||
'INSERT INTO descs (hash, description) values (%s, %s)',
|
||||
(hash, description))
|
||||
|
||||
return self.GetDescriptionID(description, 0)
|
||||
|
||||
def GetDescriptionID(self, description, auto_set = 1):
|
||||
## lame string hash, blame Netscape -JMP
|
||||
hash = len(description)
|
||||
|
||||
## attempt to retrieve from cache
|
||||
try:
|
||||
return self.dbDescriptionIDCache[hash][description]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
id = self.SQLGetDescriptionID(description, auto_set)
|
||||
if not id:
|
||||
return id
|
||||
|
||||
## add to cache
|
||||
if not self.dbDescriptionIDCache.has_key(hash):
|
||||
self.dbDescriptionIDCache[hash] = {}
|
||||
self.dbDescriptionIDCache[hash][description] = id
|
||||
return id
|
||||
|
||||
def GetDescription(self, id):
|
||||
return self.Get('descs', 'description', id)
|
||||
|
||||
def GetList(self, table, field_index):
|
||||
sql = 'SELECT * FROM %s' % (table)
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql)
|
||||
|
||||
list = []
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
list.append(row[field_index])
|
||||
|
||||
return list
|
||||
|
||||
def GetRepositoryList(self):
|
||||
return self.GetList('repositories', 1)
|
||||
|
||||
def GetBranchList(self):
|
||||
return self.GetList('branches', 1)
|
||||
|
||||
def GetAuthorList(self):
|
||||
return self.GetList('people', 1)
|
||||
|
||||
def AddCommitList(self, commit_list):
|
||||
for commit in commit_list:
|
||||
self.AddCommit(commit)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
dbType = commit.GetTypeString()
|
||||
|
||||
## MORE TIME HELL: the MySQLdb module doesn't construct times
|
||||
## correctly when created with TimestampFromTicks -- it doesn't
|
||||
## account for daylight savings time, so we use Python's time
|
||||
## module to do the conversion
|
||||
temp = time.localtime(commit.GetTime())
|
||||
|
||||
dbCI_When = dbi.Timestamp(
|
||||
temp[0], temp[1], temp[2], temp[3], temp[4], temp[5])
|
||||
|
||||
dbWhoID = self.GetAuthorID(commit.GetAuthor())
|
||||
dbRepositoryID = self.GetRepositoryID(commit.GetRepository())
|
||||
dbDirectoryID = self.GetDirectoryID(commit.GetDirectory())
|
||||
dbFileID = self.GetFileID(commit.GetFile())
|
||||
dbRevision = commit.GetRevision()
|
||||
dbStickyTag = 'NULL'
|
||||
dbBranchID = self.GetBranchID(commit.GetBranch())
|
||||
dbPlusCount = commit.GetPlusCount()
|
||||
dbMinusCount = commit.GetMinusCount()
|
||||
dbDescriptionID = self.GetDescriptionID(commit.GetDescription())
|
||||
|
||||
sql = 'REPLACE INTO checkins(type, ci_when, whoid, repositoryid, dirid, fileid, revision, stickytag, branchid, addedlines, removedlines, descid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
|
||||
sqlArguments = (
|
||||
dbType, dbCI_When, dbWhoID, dbRepositoryID, dbDirectoryID,
|
||||
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbPlusCount,
|
||||
dbMinusCount, dbDescriptionID)
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql, sqlArguments)
|
||||
|
||||
def SQLQueryListString(self, sqlString, query_entry_list):
|
||||
sqlList = []
|
||||
|
||||
for query_entry in query_entry_list:
|
||||
|
||||
## figure out the correct match type
|
||||
if query_entry.match == "exact":
|
||||
match = "="
|
||||
elif query_entry.match == "like":
|
||||
match = "LIKE"
|
||||
elif query_entry.match == "regex":
|
||||
match = "REGEXP"
|
||||
|
||||
sqlList.append(sqlString % (match, query_entry.data))
|
||||
|
||||
return "(%s)" % (string.join(sqlList, " OR "))
|
||||
|
||||
def CreateSQLQueryString(self, query):
|
||||
tableList = ['checkins']
|
||||
condList = []
|
||||
|
||||
## XXX: this is to exclude .ver files -- RN specific hack --JMP
|
||||
tableList.append("files")
|
||||
condList.append(sqlExcludeVersionFiles)
|
||||
|
||||
if len(query.repository_list):
|
||||
tableList.append("repositories")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlRepository, query.repository_list))
|
||||
|
||||
if len(query.branch_list):
|
||||
tableList.append("branches")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlBranch, query.branch_list))
|
||||
|
||||
if len(query.directory_list):
|
||||
tableList.append("dirs")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlDirectory, query.directory_list))
|
||||
|
||||
if len(query.file_list):
|
||||
tableList.append("files")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlFile, query.file_list))
|
||||
|
||||
if len(query.author_list):
|
||||
tableList.append("people")
|
||||
condList.append(
|
||||
self.SQLQueryListString(sqlAuthor, query.author_list))
|
||||
|
||||
if query.from_date:
|
||||
condList.append(sqlFromDate % (str(query.from_date)))
|
||||
|
||||
if query.to_date:
|
||||
condList.append(sqlToDate % (str(query.to_date)))
|
||||
|
||||
if query.sort == "date":
|
||||
order_by = sqlSortByDate
|
||||
elif query.sort == "author":
|
||||
order_by = sqlSortByAuthor
|
||||
elif query.sort == "file":
|
||||
order_by = sqlSortByFile
|
||||
|
||||
## exclude duplicates from the table list
|
||||
for table in tableList[:]:
|
||||
while tableList.count(table) > 1:
|
||||
tableList.remove(table)
|
||||
|
||||
sql = sqlBase % (
|
||||
string.join(tableList, ', '),
|
||||
string.join(condList, ' AND '),
|
||||
order_by)
|
||||
|
||||
return sql
|
||||
|
||||
def RunQuery(self, query):
|
||||
sql = self.CreateSQLQueryString(query)
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sql)
|
||||
|
||||
while 1:
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
break
|
||||
|
||||
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
|
||||
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
|
||||
dbRemovedLines, dbDescID) = row
|
||||
|
||||
commit = CreateCommit()
|
||||
|
||||
## TIME, TIME, TIME is all fucked up; dateobject.gmticks()
|
||||
## is broken, dateobject.ticks() returns somthing like
|
||||
## GMT ticks, except it forgets about daylight savings
|
||||
## time -- we handle it ourself in the following painful way
|
||||
gmt_time = time.mktime(
|
||||
(dbCI_When.year, dbCI_When.month, dbCI_When.day,
|
||||
dbCI_When.hour, dbCI_When.minute, dbCI_When.second,
|
||||
0, 0, dbCI_When.dst))
|
||||
|
||||
commit.SetTime(gmt_time)
|
||||
|
||||
commit.SetFile(self.GetFile(dbFileID))
|
||||
commit.SetDirectory(self.GetDirectory(dbDirID))
|
||||
commit.SetRevision(dbRevision)
|
||||
commit.SetRepository(self.GetRepository(dbRepositoryID))
|
||||
commit.SetAuthor(self.GetAuthor(dbAuthorID))
|
||||
commit.SetBranch(self.GetBranch(dbBranchID))
|
||||
commit.SetPlusCount(dbAddedLines)
|
||||
commit.SetMinusCount(dbRemovedLines)
|
||||
commit.SetDescription(self.GetDescription(dbDescID))
|
||||
|
||||
query.AddCommit(commit)
|
||||
|
||||
def CheckCommit(self, commit):
|
||||
dbRepositoryID = self.GetRepositoryID(commit.GetRepository(), 0)
|
||||
if dbRepositoryID == None:
|
||||
return None
|
||||
|
||||
dbDirID = self.GetDirectoryID(commit.GetDirectory(), 0)
|
||||
if dbDirID == None:
|
||||
return None
|
||||
|
||||
dbFileID = self.GetFileID(commit.GetFile(), 0)
|
||||
if dbFileID == None:
|
||||
return None
|
||||
|
||||
sqlArguments = (dbRepositoryID, dbDirID, dbFileID,
|
||||
commit.GetRevision())
|
||||
|
||||
cursor = self.dbConn.cursor()
|
||||
cursor.execute(sqlCheckCommit, sqlArguments)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
|
||||
return commit
|
||||
|
||||
|
||||
## entrypoints
|
||||
def CreateCheckinDatabase(host, user, passwd, database):
|
||||
return CheckinDatabase(host, user, passwd, database)
|
25
lib/dbi.py
25
lib/dbi.py
|
@ -16,27 +16,24 @@
|
|||
import sys
|
||||
import MySQLdb
|
||||
|
||||
|
||||
dbi_error = "dbi error"
|
||||
|
||||
|
||||
## make some checks in MySQLdb
|
||||
_no_datetime = """\
|
||||
ERROR: the ViewCVS database requires the mxDateTime module
|
||||
ERROR: Your version of MySQLdb requires the mxDateTime module
|
||||
for the Timestamp() and TimestampFromTicks() methods.
|
||||
You will need to install mxDateTime to use the ViewCVS
|
||||
database.
|
||||
"""
|
||||
|
||||
try:
|
||||
try:
|
||||
# new packaging
|
||||
from mx.DateTime import Timestamp, TimestampFromTicks
|
||||
except ImportError:
|
||||
# old packaging
|
||||
from DateTime import Timestamp, TimestampFromTicks
|
||||
except ImportError:
|
||||
if not hasattr(MySQLdb, "Timestamp") or \
|
||||
not hasattr(MySQLdb, "TimestampFromTicks"):
|
||||
sys.stderr.write(_no_datetime)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, mysql_cursor):
|
||||
self.__cursor = mysql_cursor
|
||||
|
@ -51,6 +48,7 @@ class Cursor:
|
|||
row = None
|
||||
|
||||
return row
|
||||
|
||||
|
||||
class Connection:
|
||||
def __init__(self, host, user, passwd, db):
|
||||
|
@ -60,5 +58,14 @@ class Connection:
|
|||
def cursor(self):
|
||||
return Cursor(self.__mysql.cursor())
|
||||
|
||||
|
||||
def Timestamp(year, month, date, hour, minute, second):
|
||||
return MySQLdb.Timestamp(year, month, date, hour, minute, second)
|
||||
|
||||
|
||||
def TimestampFromTicks(ticks):
|
||||
return MySQLdb.TimestampFromTicks(ticks)
|
||||
|
||||
|
||||
def connect(host, user, passwd, db):
|
||||
return Connection(host, user, passwd, db)
|
||||
|
|
631
lib/query.py
631
lib/query.py
|
@ -1,102 +1,569 @@
|
|||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# CGI script to process and display queries to CVSdb
|
||||
#
|
||||
# This script is part of the ViewCVS package. More information can be
|
||||
# found at http://www.lyra.org/viewcvs/.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
||||
#########################################################################
|
||||
#
|
||||
# INSTALL-TIME CONFIGURATION
|
||||
#
|
||||
# These values will be set during the installation process. During
|
||||
# development, they will remain None.
|
||||
#
|
||||
|
||||
CONF_PATHNAME = None
|
||||
|
||||
#########################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import cgi
|
||||
import time
|
||||
import dbi
|
||||
import traceback
|
||||
|
||||
import cvsdb
|
||||
import viewcvs
|
||||
|
||||
## QueryEntry holds data on one match-type in the SQL database
|
||||
## match is: "exact", "like", or "regex"
|
||||
|
||||
class QueryEntry:
|
||||
def __init__(self, data, match):
|
||||
self.data = data
|
||||
self.match = match
|
||||
|
||||
|
||||
## CheckinDatabaseQueryData is a object which contains the search parameters
|
||||
## for a query to the CheckinDatabase
|
||||
class FormData:
|
||||
def __init__(self, form):
|
||||
self.valid = 0
|
||||
|
||||
class CheckinDatabaseQuery:
|
||||
def __init__(self):
|
||||
## sorting
|
||||
self.sort = "date"
|
||||
self.repository = ""
|
||||
self.branch = ""
|
||||
self.directory = ""
|
||||
self.file = ""
|
||||
self.who = ""
|
||||
self.sortby = ""
|
||||
self.date = ""
|
||||
self.hours = 0
|
||||
|
||||
self.decode_thyself(form)
|
||||
|
||||
## repository to query
|
||||
self.repository_list = []
|
||||
self.branch_list = []
|
||||
self.directory_list = []
|
||||
self.file_list = []
|
||||
self.author_list = []
|
||||
|
||||
## date range in DBI 2.0 timedate objects
|
||||
self.from_date = None
|
||||
self.to_date = None
|
||||
|
||||
## list of commits -- filled in by CVS query
|
||||
self.commit_list = []
|
||||
|
||||
## commit_cb provides a callback for commits as they
|
||||
## are added
|
||||
self.commit_cb = None
|
||||
|
||||
def SetRepository(self, repository, match = "exact"):
|
||||
self.repository_list.append(QueryEntry(repository, match))
|
||||
|
||||
def SetBranch(self, branch, match = "exact"):
|
||||
self.branch_list.append(QueryEntry(branch, match))
|
||||
|
||||
def SetDirectory(self, directory, match = "exact"):
|
||||
self.directory_list.append(QueryEntry(directory, match))
|
||||
|
||||
def SetFile(self, file, match = "exact"):
|
||||
self.file_list.append(QueryEntry(file, match))
|
||||
|
||||
def SetAuthor(self, author, match = "exact"):
|
||||
self.author_list.append(QueryEntry(author, match))
|
||||
|
||||
def SetSortMethod(self, sort):
|
||||
self.sort = sort
|
||||
|
||||
def SetFromDateObject(self, ticks):
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateObject(self, ticks):
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetFromDateHoursAgo(self, hours_ago):
|
||||
ticks = time.time() - (3600 * hours_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
def decode_thyself(self, form):
|
||||
try:
|
||||
self.repository = string.strip(form["repository"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
def SetFromDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.from_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def SetToDateDaysAgo(self, days_ago):
|
||||
ticks = time.time() - (86400 * days_ago)
|
||||
self.to_date = dbi.TimestampFromTicks(ticks)
|
||||
|
||||
def AddCommit(self, commit):
|
||||
self.commit_list.append(commit)
|
||||
if self.commit_cb:
|
||||
self.commit_cb(commit)
|
||||
try:
|
||||
self.branch = string.strip(form["branch"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.directory = string.strip(form["directory"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.file = string.strip(form["file"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.who = string.strip(form["who"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
try:
|
||||
self.sortby = string.strip(form["sortby"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def SetCommitCB(self, callback):
|
||||
self.commit_cb = callback
|
||||
|
||||
try:
|
||||
self.date = string.strip(form["date"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.hours = int(form["hours"].value)
|
||||
except KeyError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.valid = 1
|
||||
|
||||
## returns a tuple-list (mod-str, string)
|
||||
def listparse_string(str):
|
||||
return_list = []
|
||||
|
||||
## entrypoints
|
||||
def CreateCheckinQuery():
|
||||
return CheckinDatabaseQuery()
|
||||
cmd = ""
|
||||
temp = ""
|
||||
escaped = 0
|
||||
state = "eat leading whitespace"
|
||||
|
||||
for c in str:
|
||||
## handle escaped charactors
|
||||
if not escaped and c == "\\":
|
||||
escaped = 1
|
||||
continue
|
||||
|
||||
## strip leading white space
|
||||
if state == "eat leading whitespace":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
else:
|
||||
state = "get command or data"
|
||||
|
||||
## parse to '"' or ","
|
||||
if state == "get command or data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## the data is in quotes after the command
|
||||
elif c == "\"":
|
||||
cmd = temp
|
||||
temp = ""
|
||||
state = "get quoted data"
|
||||
continue
|
||||
|
||||
## this tells us there was no quoted data, therefore no
|
||||
## command; add the command and start over
|
||||
elif c == ",":
|
||||
## strip ending whitespace on un-quoted data
|
||||
temp = string.rstrip(temp)
|
||||
return_list.append( ("", temp) )
|
||||
temp = ""
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## parse until ending '"'
|
||||
if state == "get quoted data":
|
||||
|
||||
## just add escaped charactors
|
||||
if escaped:
|
||||
escaped = 0
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## look for ending '"'
|
||||
elif c == "\"":
|
||||
return_list.append( (cmd, temp) )
|
||||
cmd = ""
|
||||
temp = ""
|
||||
state = "eat comma after quotes"
|
||||
continue
|
||||
|
||||
## record the data
|
||||
else:
|
||||
temp = temp + c
|
||||
continue
|
||||
|
||||
## parse until ","
|
||||
if state == "eat comma after quotes":
|
||||
if c in string.whitespace:
|
||||
continue
|
||||
|
||||
elif c == ",":
|
||||
state = "eat leading whitespace"
|
||||
continue
|
||||
|
||||
else:
|
||||
print "format error"
|
||||
sys.exit(1)
|
||||
|
||||
if cmd or temp:
|
||||
return_list.append((cmd, temp))
|
||||
|
||||
return return_list
|
||||
|
||||
def decode_command(cmd):
|
||||
if cmd == "r":
|
||||
return "regex"
|
||||
elif cmd == "l":
|
||||
return "like"
|
||||
else:
|
||||
return "exact"
|
||||
|
||||
def form_to_cvsdb_query(form_data):
|
||||
query = cvsdb.CreateCheckinQuery()
|
||||
|
||||
if form_data.repository:
|
||||
for cmd, str in listparse_string(form_data.repository):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetRepository(str, cmd)
|
||||
|
||||
if form_data.branch:
|
||||
for cmd, str in listparse_string(form_data.branch):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetBranch(str, cmd)
|
||||
|
||||
if form_data.directory:
|
||||
for cmd, str in listparse_string(form_data.directory):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetDirectory(str, cmd)
|
||||
|
||||
if form_data.file:
|
||||
for cmd, str in listparse_string(form_data.file):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetFile(str, cmd)
|
||||
|
||||
if form_data.who:
|
||||
for cmd, str in listparse_string(form_data.who):
|
||||
cmd = decode_command(cmd)
|
||||
query.SetAuthor(str, cmd)
|
||||
|
||||
if form_data.sortby == "author":
|
||||
query.SetSortMethod("author")
|
||||
elif form_data.sortby == "file":
|
||||
query.SetSortMethod("file")
|
||||
else:
|
||||
query.SetSortMethod("date")
|
||||
|
||||
if form_data.date:
|
||||
if form_data.date == "hours" and form_data.hours:
|
||||
query.SetFromDateHoursAgo(form_data.hours)
|
||||
elif form_data.date == "day":
|
||||
query.SetFromDateDaysAgo(1)
|
||||
elif form_data.date == "week":
|
||||
query.SetFromDateDaysAgo(7)
|
||||
elif form_data.date == "month":
|
||||
query.SetFromDateDaysAgo(31)
|
||||
|
||||
return query
|
||||
|
||||
def cvsroot_name_from_path(cvsroot):
|
||||
## we need to resolve the cvsroot path from the database
|
||||
## to the name given to it in the viewcvs.conf file
|
||||
cvsroot_name = ""
|
||||
for (key, value) in cfg.general.cvs_roots.items():
|
||||
if value == cvsroot:
|
||||
cvsroot_name = key
|
||||
break
|
||||
return cvsroot_name
|
||||
|
||||
def html_commit_list(commit_list, color):
|
||||
rs = len(commit_list)
|
||||
|
||||
## one description for these commits
|
||||
desc = commit_list[0].GetDescription()
|
||||
if not desc:
|
||||
desc = " ";
|
||||
else:
|
||||
desc = cgi.escape(desc)
|
||||
desc = string.replace(desc, "\n", "<br>")
|
||||
|
||||
for commit in commit_list:
|
||||
ctime = commit.GetTime()
|
||||
if not ctime:
|
||||
ctime = " ";
|
||||
else:
|
||||
ctime = time.strftime("%y/%m/%d %H:%M", time.localtime(ctime))
|
||||
|
||||
author = commit.GetAuthor() or " "
|
||||
|
||||
## make the file link
|
||||
file = os.path.join(commit.GetDirectory(), commit.GetFile())
|
||||
file_full_path = os.path.join(commit.GetRepository(), file)
|
||||
|
||||
## if we couldn't find the cvsroot path configured in the
|
||||
## viewcvs.conf file, then don't make the link
|
||||
cvsroot_name = cvsroot_name_from_path(commit.GetRepository())
|
||||
if cvsroot_name:
|
||||
flink = "<a href=\"viewcvs.cgi/%s?cvsroot=%s\">%s</a>" % (
|
||||
file, cvsroot_name, file_full_path)
|
||||
else:
|
||||
flink = file_full_path
|
||||
|
||||
revision = commit.GetRevision() or " "
|
||||
branch = commit.GetBranch() or " "
|
||||
plusminus = "%d/%d" % (commit.GetPlusCount(), commit.GetMinusCount())
|
||||
|
||||
print "<tr bgcolor=%s>" % (color)
|
||||
print "<td align=left valign=top>%s</td>" % (ctime)
|
||||
print "<td align=left valign=top>%s</td>" % (author)
|
||||
print "<td align=left valign=top>%s</td>" % (flink)
|
||||
print "<td align=left valign=top>%s</td>" % (revision)
|
||||
print "<td align=left valign=top>%s</td>" % (branch)
|
||||
print "<td align=left valign=top>%s</td>" % (plusminus)
|
||||
|
||||
if commit == commit_list[0]:
|
||||
print "<td align=left valign=top rowspan=%d>%s</td>" % (rs,desc)
|
||||
print "</tr>"
|
||||
|
||||
def run_query(form_data):
|
||||
query = form_to_cvsdb_query(form_data)
|
||||
db = cvsdb.ConnectDatabaseReadOnly()
|
||||
db.RunQuery(query)
|
||||
|
||||
print "<p><b>%d</b> matches found.</p>" % (len(query.commit_list))
|
||||
|
||||
print "<table width=100%% border=0 cellspacing=0 cellpadding=2>"
|
||||
print " <tr bgcolor=#88ff88>"
|
||||
print " <th align=left valign=top>Date</th>"
|
||||
print " <th align=left valign=top>Author</th>"
|
||||
print " <th align=left valign=top>File</th>"
|
||||
print " <th align=left valign=top>Revision</th>"
|
||||
print " <th align=left valign=top>Branch</th>"
|
||||
print " <th align=left valign=top>+/-</th>"
|
||||
print " <th align=left valign=top>Description</th>"
|
||||
print " </tr>"
|
||||
|
||||
commit_num = 0
|
||||
commit_list = []
|
||||
current_desc = None
|
||||
|
||||
if len(query.commit_list):
|
||||
current_desc = query.commit_list[0].GetDescription()
|
||||
|
||||
for commit in query.commit_list:
|
||||
desc = commit.GetDescription()
|
||||
|
||||
if current_desc == desc:
|
||||
commit_list.append(commit)
|
||||
continue
|
||||
|
||||
html_commit_list(commit_list, cfg.colors.even_odd[commit_num % 2])
|
||||
commit_list = [commit]
|
||||
current_desc = desc
|
||||
commit_num = commit_num + 1
|
||||
|
||||
## display the last commit_list
|
||||
if len(commit_list):
|
||||
html_commit_list(commit_list, cfg.colors.even_odd[commit_num % 2])
|
||||
|
||||
print " <tr bgcolor=#88ff88>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " <th align=left valign=top> </th>"
|
||||
print " </tr>"
|
||||
print "</table>"
|
||||
|
||||
def html_left_form_table(form_data):
|
||||
rp = string.replace(cgi.escape(form_data.repository), "\"", """)
|
||||
br = string.replace(cgi.escape(form_data.branch), "\"", """)
|
||||
di = string.replace(cgi.escape(form_data.directory), "\"", """)
|
||||
fi = string.replace(cgi.escape(form_data.file), "\"", """)
|
||||
wh = string.replace(cgi.escape(form_data.who), "\"", """)
|
||||
|
||||
print "<table>"
|
||||
print " <tr>"
|
||||
print " <td align=right>CVS Repository:</td>"
|
||||
print " <td>"
|
||||
print " <input type=text name=repository size=40 value=\"%s\">" % (rp)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td align=right>CVS Branch:</td>"
|
||||
print " <td>"
|
||||
print " <input type=text name=branch size=40 value=\"%s\">" % (br)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td align=right>Directory:</td>"
|
||||
print " <td>"
|
||||
print " <input type=text name=directory size=40 value=\"%s\">" % (di)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td align=right>File:</td>"
|
||||
print " <td>"
|
||||
print " <input type=text name=file size=40 value=\"%s\">" % (fi)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td align=right>Author:</td>"
|
||||
print " <td>"
|
||||
print " <input type=text name=who size=40 value=\"%s\">" % (wh)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print "</table>"
|
||||
|
||||
def html_right_form_table(form_data):
|
||||
fs = ""
|
||||
as = ""
|
||||
ds = ""
|
||||
if form_data.sortby == "file":
|
||||
fs = "selected"
|
||||
elif form_data.sortby == "author":
|
||||
as = "selected"
|
||||
else:
|
||||
ds = "selected"
|
||||
|
||||
hr = 2
|
||||
if form_data.hours:
|
||||
hr = form_data.hours
|
||||
|
||||
hc = ""
|
||||
dc = ""
|
||||
wc = ""
|
||||
mc = ""
|
||||
ac = ""
|
||||
if form_data.date == "month":
|
||||
mc = "checked"
|
||||
elif form_data.date == "week":
|
||||
wc = "checked"
|
||||
elif form_data.date == "day":
|
||||
dc = "checked"
|
||||
elif form_data.date == "all":
|
||||
ac = "checked"
|
||||
else:
|
||||
hc = "checked"
|
||||
|
||||
print "<table>"
|
||||
print " <tr>"
|
||||
print " <td align=left>Sort By:</td>"
|
||||
print " <td>"
|
||||
print " <select name=sortby>"
|
||||
print " <option value=date %s>Date</option>" % (ds)
|
||||
print " <option value=author %s>Author</option>" % (as)
|
||||
print " <option value=file %s>File</option>" % (fs)
|
||||
print " </select>"
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td colspan=2>"
|
||||
print " <table border=0 cellspacing=0 cellpadding=0>"
|
||||
print " <tr>"
|
||||
print " <td>Date:</td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td><input type=radio name=date value=hours %s></td>" % (hc)
|
||||
print " <td>In the last"
|
||||
print " <input type=text name=hours value=%d size=4>hours" % (hr)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td><input type=radio name=date value=day %s></td>" % (dc)
|
||||
print " <td>In the last day</td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td><input type=radio name=date value=week %s></td>" % (wc)
|
||||
print " <td>In the last week</td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td><input type=radio name=date value=month %s></td>" % (mc)
|
||||
print " <td>In the last month</td>"
|
||||
print " </tr>"
|
||||
print " <tr>"
|
||||
print " <td><input type=radio name=date value=all %s></td>" % (ac)
|
||||
print " <td>Since the beginning of time</td>"
|
||||
print " </tr>"
|
||||
print " </table>"
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print "</table>"
|
||||
|
||||
def html_form(form_data):
|
||||
print "<form method=get action=\"query.cgi\">"
|
||||
|
||||
print "<table border=0 cellspacing=0 cellpadding=2 "\
|
||||
" width=100%% bgcolor=e6e6e6>"
|
||||
print " <tr>"
|
||||
print " <td>"
|
||||
print " <table>"
|
||||
print " <tr>"
|
||||
print " <td valign=top>"
|
||||
html_left_form_table(form_data)
|
||||
print " </td>"
|
||||
print " <td valign=top>"
|
||||
html_right_form_table(form_data)
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print " </table>"
|
||||
print " </td>"
|
||||
print " <td>"
|
||||
print " <input type=submit value=\"Search\">"
|
||||
print " </td>"
|
||||
print " </tr>"
|
||||
print "</table>"
|
||||
|
||||
print "</form>"
|
||||
|
||||
def html_header(title):
|
||||
viewcvs.html_header(title)
|
||||
print cfg.text.cvsdb_intro
|
||||
|
||||
def handle_config():
|
||||
viewcvs.handle_config()
|
||||
global cfg
|
||||
cfg = viewcvs.cfg
|
||||
|
||||
def main():
|
||||
handle_config()
|
||||
|
||||
form = cgi.FieldStorage()
|
||||
form_data = FormData(form)
|
||||
|
||||
html_header(cfg.general.main_title)
|
||||
html_form(form_data)
|
||||
|
||||
if form_data.valid:
|
||||
run_query(form_data)
|
||||
|
||||
viewcvs.html_footer()
|
||||
|
||||
def run_cgi():
|
||||
try:
|
||||
main()
|
||||
except SystemExit, e:
|
||||
# don't catch SystemExit (caused by sys.exit()). propagate the exit code
|
||||
sys.exit(e[0])
|
||||
except:
|
||||
info = sys.exc_info()
|
||||
viewcvs.html_header('Python Exception Occurred')
|
||||
import traceback
|
||||
lines = apply(traceback.format_exception, info)
|
||||
print '<pre>'
|
||||
print cgi.escape(string.join(lines, ''))
|
||||
print '</pre>'
|
||||
viewcvs.html_footer()
|
||||
|
|
|
@ -25,7 +25,12 @@ CONF_PATHNAME = None
|
|||
|
||||
#########################################################################
|
||||
|
||||
import os, sys, string, re, time, config
|
||||
import os
|
||||
import sys
|
||||
import string
|
||||
import re
|
||||
import time
|
||||
import config
|
||||
|
||||
## load configuration file, the data is used globally here
|
||||
cfg = config.Config()
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@ -40,13 +40,15 @@ else:
|
|||
|
||||
#########################################################################
|
||||
|
||||
import os, string, cvsdbapi
|
||||
import os
|
||||
import string
|
||||
import cvsdb
|
||||
|
||||
|
||||
def UpdateFile(db, repository, path):
|
||||
try:
|
||||
commit_list = cvsdbapi.GetUnrecordedCommitList(repository, path)
|
||||
except cvsdbapi.error, e:
|
||||
commit_list = cvsdb.GetUnrecordedCommitList(repository, path)
|
||||
except cvsdb.error, e:
|
||||
print '[ERROR] %s' % (e)
|
||||
return
|
||||
|
||||
|
@ -78,15 +80,15 @@ def RecurseUpdate(db, repository, directory):
|
|||
|
||||
def CommandUpdate():
|
||||
## connect to the database we are updating
|
||||
db = cvsdbapi.ConnectDatabase()
|
||||
db = cvsdb.ConnectDatabase()
|
||||
repository = sys.argv[2]
|
||||
RecurseUpdate(db, repository, repository)
|
||||
|
||||
|
||||
def RebuildFile(db, repository, path):
|
||||
try:
|
||||
commit_list = cvsdbapi.GetCommitListFromRCSFile(repository, path)
|
||||
except cvsdbapi.error, e:
|
||||
commit_list = cvsdb.GetCommitListFromRCSFile(repository, path)
|
||||
except cvsdb.error, e:
|
||||
print '[ERROR] %s' % (e)
|
||||
return
|
||||
|
||||
|
@ -118,7 +120,7 @@ def RecurseRebuild(db, repository, directory):
|
|||
|
||||
def CommandRebuild():
|
||||
## connect to the database we are updating
|
||||
db = cvsdbapi.ConnectDatabase()
|
||||
db = cvsdb.ConnectDatabase()
|
||||
repository = sys.argv[2]
|
||||
RecurseRebuild(db, repository, repository)
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#!/usr/bin/python
|
||||
# -*- Mode: python -*-
|
||||
#
|
||||
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
||||
#
|
||||
# By using this file, you agree to the terms and conditions set forth in
|
||||
# the LICENSE.html file which can be found at the top level of the ViewCVS
|
||||
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
|
||||
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
|
||||
#
|
||||
# Contact information:
|
||||
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
||||
# gstein@lyra.org, http://viewcvs.sourceforge.net/
|
||||
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
|
@ -39,14 +39,19 @@ else:
|
|||
|
||||
#########################################################################
|
||||
|
||||
import os, string, getopt, re, cvsdbapi
|
||||
import os
|
||||
import string
|
||||
import getopt
|
||||
import re
|
||||
import cvsdb
|
||||
import rlog
|
||||
|
||||
|
||||
DEBUG_FLAG = 0
|
||||
|
||||
## pre-compiled regular expressions
|
||||
_re_fileversion = re.compile("([^,]+)\,([^,]+)\,([^,]+)")
|
||||
|
||||
|
||||
## output functions
|
||||
def debug(text):
|
||||
if DEBUG_FLAG:
|
||||
|
@ -59,13 +64,7 @@ def error(text):
|
|||
print 'ERROR(loginfo): %s' % (text)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class FileData:
|
||||
|
||||
CHANGED = 1
|
||||
ADDED = 2
|
||||
REMOVED = 3
|
||||
|
||||
def __init__(self, file, directory, old_version, new_version):
|
||||
self.file = file
|
||||
self.directory = directory
|
||||
|
@ -75,12 +74,11 @@ class FileData:
|
|||
## set the state of this file from the
|
||||
## old_version and new_version information
|
||||
if self.old_version == 'NONE':
|
||||
self.type = self.ADDED
|
||||
self.ctype = "added"
|
||||
elif self.new_version == 'NONE':
|
||||
self.type = self.REMOVED
|
||||
self.ctype = "removed"
|
||||
else:
|
||||
self.type = self.CHANGED
|
||||
|
||||
self.ctype = "changed"
|
||||
|
||||
def CommitFromFileData(repository, file_data):
|
||||
## consturct the full path for the RCS file
|
||||
|
@ -88,25 +86,23 @@ def CommitFromFileData(repository, file_data):
|
|||
|
||||
## get the 'rlog' output for just this revision, and then convert
|
||||
## to a commit object
|
||||
rlog_data = cvsdbapi.GetRLogData(filename, file_data.new_version)
|
||||
commit_list = cvsdbapi.RLogDataToCommitList(repository, rlog_data)
|
||||
rlog_data = rlog.GetRLogData(filename, file_data.new_version)
|
||||
commit_list = cvsdb.RLogDataToCommitList(repository, rlog_data)
|
||||
commit = commit_list[0]
|
||||
|
||||
## set the type of commit from the file_data setting
|
||||
if file_data.type == file_data.CHANGED:
|
||||
if file_data.ctype == "changed":
|
||||
commit.SetTypeChange()
|
||||
elif file_data.type == file_data.ADDED:
|
||||
elif file_data.ctype == "added":
|
||||
commit.SetTypeAdd()
|
||||
elif file_data.type == file_data.REMOVED:
|
||||
elif file_data.ctype == "removed":
|
||||
commit.SetTypeRemove()
|
||||
|
||||
return commit
|
||||
|
||||
|
||||
def GetUnrecordedCommitList(repository, file_data):
|
||||
filename = os.path.join(repository, file_data.directory, file_data.file)
|
||||
return cvsdbapi.GetUnrecordedCommitList(repository, filename)
|
||||
|
||||
return cvsdb.GetUnrecordedCommitList(repository, filename)
|
||||
|
||||
def ProcessLoginfo(repository, stdin_list):
|
||||
## the first line in stdin is a space-separated list; the first
|
||||
|
@ -155,14 +151,14 @@ def ProcessLoginfo(repository, stdin_list):
|
|||
## given enough information to find it in the rlog output!
|
||||
## So instead, we rlog everything in the removed file, and
|
||||
## add any commits not already in the database
|
||||
if file_data.type == file_data.REMOVED:
|
||||
if file_data.ctype == "removed":
|
||||
temp = GetUnrecordedCommitList(repository, file_data)
|
||||
commit_list = commit_list + temp
|
||||
else:
|
||||
commit_list.append(CommitFromFileData(repository, file_data))
|
||||
|
||||
## add to the database
|
||||
db = cvsdbapi.ConnectDatabase()
|
||||
db = cvsdb.ConnectDatabase()
|
||||
db.AddCommitList(commit_list)
|
||||
|
||||
|
||||
|
|
|
@ -54,18 +54,15 @@ ROOT_DIR = "/usr/local/viewcvs-dev"
|
|||
|
||||
FILE_INFO_LIST = [
|
||||
("cgi/viewcvs.cgi", "cgi/viewcvs.cgi", 0755, 1, 0, 0),
|
||||
("cgi/queryform.cgi", "cgi/queryform.cgi", 0755, 1, 0, 0),
|
||||
("cgi/query.cgi", "cgi/query.cgi", 0755, 1, 0, 0),
|
||||
|
||||
("cgi/viewcvs.conf.dist", "viewcvs.conf", 0644, 0, 1, 0),
|
||||
|
||||
("lib/PyFontify.py", "lib/PyFontify.py", 0644, 0, 0, 1),
|
||||
("lib/blame.py", "lib/blame.py", 0644, 0, 0, 1),
|
||||
("lib/commit.py", "lib/commit.py", 0644, 0, 0, 1),
|
||||
("lib/compat.py", "lib/compat.py", 0644, 0, 0, 1),
|
||||
("lib/config.py", "lib/config.py", 0644, 0, 0, 1),
|
||||
("lib/cvsdbapi.py", "lib/cvsdbapi.py", 0644, 1, 0, 1),
|
||||
("lib/database.py", "lib/database.py", 0644, 0, 0, 1),
|
||||
("lib/cvsdb.py", "lib/cvsdb.py", 0644, 1, 0, 1),
|
||||
("lib/dbi.py", "lib/dbi.py", 0644, 0, 0, 1),
|
||||
("lib/popen.py", "lib/popen.py", 0644, 0, 0, 1),
|
||||
("lib/py2html.py", "lib/py2html.py", 0644, 0, 0, 1),
|
||||
|
@ -77,11 +74,6 @@ FILE_INFO_LIST = [
|
|||
("tools/loginfo-handler", "loginfo-handler", 0755, 1, 0, 0),
|
||||
("tools/cvsdbadmin", "cvsdbadmin", 0755, 1, 0, 0),
|
||||
("tools/make-database", "make-database", 0755, 1, 0, 0),
|
||||
|
||||
("html-templates/queryformtemplate.html",
|
||||
"html-templates/queryformtemplate.html", 0644, 0, 1, 0),
|
||||
("html-templates/querytemplate.html",
|
||||
"html-templates/querytemplate.html", 0644, 0, 1, 0),
|
||||
]
|
||||
|
||||
|
||||
|
@ -119,7 +111,6 @@ def SetPythonPaths(contents):
|
|||
contents = re.sub('^#![^\n]*', shbang, contents)
|
||||
contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib')
|
||||
contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewcvs.conf')
|
||||
contents = SetOnePath(contents, 'HTML_TEMPLATE_DIR', 'html-templates')
|
||||
return contents
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue