1 // vim:ts=4:sw=4:noexpandtab
10 "github.com/mstap/godebiancontrol"
16 var mirrorUrl
= flag
.String("mirror_url",
17 "http://ftp.ch.debian.org/debian/",
18 "URL to the debian mirror to use")
19 var dryRun
= flag
.Bool("dry_run", false, "Don’t actually write anything to the database.")
20 var verbose
= flag
.Bool("verbose", false, "Print ranking information about every package")
21 var popconInstSrc
map[string]float32 = make(map[string]float32)
23 // Fills popconInstSrc from the Ultimate Debian Database (udd). The popcon
24 // installation count is stored normalized by dividing through the total amount
25 // of popcon installations.
26 func fillPopconInst() {
27 db
, err
:= sql
.Open("postgres", "dbname=udd host=/var/run/postgresql/ sslmode=disable")
32 var totalInstallations
int
33 var packageName
string
35 err
= db
.QueryRow("SELECT SUM(insts) FROM popcon").Scan(&totalInstallations
)
37 log
.Fatalf("Could not get SUM(insts) FROM popcon: %v", err
)
41 log
.Printf("total %d installations", totalInstallations
)
44 rows
, err
:= db
.Query("SELECT source, insts FROM popcon_src")
49 if err
= rows
.Scan(&packageName
, &installations
); err
!= nil {
53 // XXX: We multiply 1000 here because all values are < 0.0009.
54 popconInstSrc
[packageName
] = (float32(installations
) / float32(totalInstallations
)) * 1000
59 func mustLoadMirroredControlFile(name
string) []godebiancontrol
.Paragraph
{
60 url
:= fmt
.Sprintf("%s/dists/sid/main/%s", *mirrorUrl
, name
)
61 resp
, err
:= http
.Get(url
)
65 if resp
.StatusCode
!= 200 {
66 log
.Fatalf("URL %q resulted in %v\n", url
, resp
.Status
)
68 defer resp
.Body
.Close()
70 reader
, err
:= gzip
.NewReader(resp
.Body
)
74 contents
, err
:= godebiancontrol
.Parse(reader
)
87 db
, err
:= sql
.Open("postgres", "dbname=dcs host=/var/run/postgresql/ sslmode=disable")
92 insert
, err
:= db
.Prepare("INSERT INTO pkg_ranking (package, popcon, rdepends) VALUES ($1, $2, $3)")
98 update
, err
:= db
.Prepare("UPDATE pkg_ranking SET popcon = $2, rdepends = $3 WHERE package = $1")
104 sourcePackages
:= mustLoadMirroredControlFile("source/Sources.gz")
105 binaryPackages
:= mustLoadMirroredControlFile("binary-amd64/Packages.gz")
107 reverseDeps
:= make(map[string]uint)
108 for _
, pkg
:= range binaryPackages
{
109 // We need to filter duplicates, because consider this:
110 // agda-bin Recommends: libghc-agda-dev (>= 2.3.2), libghc-agda-dev (<< 2.3.2)
111 dependsOn
:= make(map[string]bool)
112 // NB: This differs from what apt-cache rdepends spit out. apt-cache
113 // also considers the Replaces field.
114 allDeps
:= pkg
["Depends"] + "," + pkg
["Suggests"] + "," + pkg
["Recommends"] + "," + pkg
["Enhances"]
115 for _
, dep
:= range strings
.FieldsFunc(allDeps
, func(r rune
) bool {
116 return r
== ',' || r
== '|'
118 trimmed
:= strings
.TrimSpace(dep
)
119 spaceIdx
:= strings
.Index(trimmed
, " ")
121 spaceIdx
= len(trimmed
)
123 dependsOn
[trimmed
[:spaceIdx
]] = true
125 for name
, _
:= range dependsOn
{
126 reverseDeps
[name
] += 1
130 for _
, pkg
:= range sourcePackages
{
131 rdepcount
:= float32(0)
132 for _
, packageName
:= range strings
.Split(pkg
["Binary"], ",") {
133 packageName
= strings
.TrimSpace(packageName
)
134 if packageName
== "" {
137 rdepcount
+= float32(reverseDeps
[packageName
])
139 srcpkg
:= pkg
["Package"]
140 packageRank
:= popconInstSrc
[srcpkg
]
141 rdepcount
= 1.0 - (1.0 / float32(rdepcount
+1))
143 fmt
.Printf("%f %f %s\n", packageRank
, rdepcount
, srcpkg
)
148 result
, err
:= update
.Exec(srcpkg
, packageRank
, rdepcount
)
150 // The UPDATE succeeded, but let’s see whether it affected a row.
152 affected
, err
= result
.RowsAffected()
154 if err
!= nil || affected
== 0 {
155 if _
, err
= insert
.Exec(srcpkg
, packageRank
, rdepcount
); err
!= nil {