1"""Queries related to source packages
3@contact: Debian FTPMaster <ftpmaster@debian.org>
4@copyright: 2014 Mark Hymers <mhy@debian.org>
5@copyright: 2014 Joerg Jaspert <joerg@debian.org>
6@license: GNU General Public License version 2 or later
7"""
9import json
10from typing import Optional
12import bottle
13from sqlalchemy import or_
15from daklib.dbconn import (
16 DBConn,
17 DBSource,
18 DSCFile,
19 MetadataKey,
20 PoolFile,
21 SourceMetadata,
22 Suite,
23)
24from dakweb.webregister import QueryRegister
27@bottle.route("/dsc_in_suite/<suite>/<source>")
28def dsc_in_suite(suite: Optional[str] = None, source: Optional[str] = None) -> str:
29 """
30 Find all dsc files for a given source package name in a given suite.
32 .. versionadded: December 2014
34 :param suite: Name of the suite.
35 :param source: Source package to query for.
36 :return: List of dictionaries made out of
37 - version
38 - component
39 - filename
40 - filesize
41 - sha256sum
43 .. seealso:: :func:`~dakweb.queries.suite.suites` on how to receive a list of valid suites.
44 """
45 if suite is None:
46 return bottle.HTTPError(503, "Suite not specified.")
47 if source is None:
48 return bottle.HTTPError(503, "Source package not specified.")
50 s = DBConn().session()
51 q = s.query(DSCFile).join(PoolFile)
52 q = q.join(DBSource).join(Suite, DBSource.suites)
53 q = q.filter(or_(Suite.suite_name == suite, Suite.codename == suite))
54 q = q.filter(DBSource.source == source)
55 q = q.filter(PoolFile.filename.endswith(".dsc"))
56 ret = []
57 for p in q:
58 ret.append(
59 {
60 "version": p.source.version,
61 "component": p.poolfile.component.component_name,
62 "filename": p.poolfile.filename,
63 "filesize": p.poolfile.filesize,
64 "sha256sum": p.poolfile.sha256sum,
65 }
66 )
68 s.close()
70 bottle.response.content_type = "application/json; charset=UTF-8"
71 return json.dumps(ret)
74QueryRegister().register_path("/dsc_in_suite", dsc_in_suite)
77@bottle.route("/file_in_archive/<filepattern:path>")
78def file_in_archive(filepattern: Optional[str] = None) -> str:
79 """
80 Check if a file pattern is known to the archive. Note that the
81 patterns are matched against the location of the files in the
82 pool, so for %tmux_2.3-1.dsc it will return t/tmux/tmux_2.3-1.dsc
83 as filename.
85 .. versionadded:: October 2016
87 :param filepattern: Pattern of the filenames to match. SQL LIKE
88 statement wildcard matches are supported, that
89 is % for zero, one or more characters, _ for a
90 single character match.
91 :return: List of dictionaries made out of
92 - filename
93 - sha256sum
94 - component
95 """
96 if filepattern is None:
97 return bottle.HTTPError(503, "Filepattern not specified.")
99 s = DBConn().session()
100 q = s.query(PoolFile)
101 q = q.filter(PoolFile.filename.like(filepattern))
102 ret = []
104 for p in q:
105 ret.append(
106 {
107 "filename": p.filename,
108 "component": p.component.component_name,
109 "sha256sum": p.sha256sum,
110 }
111 )
113 s.close()
115 bottle.response.content_type = "application/json; charset=UTF-8"
116 return json.dumps(ret)
119QueryRegister().register_path("/file_in_archive", file_in_archive)
122@bottle.route("/sha256sum_in_archive/<sha256sum>")
123def sha256sum_in_archive(sha256sum: Optional[str] = None) -> str:
124 """
125 Check if files with matching sha256sums are known to the archive.
127 .. versionadded:: June 2018
129 :param sha256sum: SHA256 sum of the file.
130 :return: List of dictionaries made out of
131 - filename
132 - sha256sum
133 - component
134 """
135 if sha256sum is None:
136 return bottle.HTTPError(503, "sha256sum not specified.")
138 s = DBConn().session()
139 q = s.query(PoolFile)
140 q = q.filter(PoolFile.sha256sum == sha256sum)
141 ret = []
143 for p in q:
144 ret.append(
145 {
146 "filename": p.filename,
147 "component": p.component.component_name,
148 "sha256sum": p.sha256sum,
149 }
150 )
152 s.close()
154 bottle.response.content_type = "application/json; charset=UTF-8"
155 return json.dumps(ret)
158QueryRegister().register_path("/sha256sum_in_archive", sha256sum_in_archive)
161@bottle.route("/sources_in_suite/<suite>")
162def sources_in_suite(suite: Optional[str] = None) -> str:
163 """
164 Returns all source packages and their versions in a given suite.
166 .. versionadded:: December 2014
168 :param suite: Name of the suite.
169 :return: List of dictionaries made out of
170 - source
171 - version
173 .. seealso:: :func:`~dakweb.queries.suite.suites` on how to receive a list of valid suites.
174 """
175 if suite is None:
176 return bottle.HTTPError(503, "Suite not specified.")
178 s = DBConn().session()
179 q = s.query(DBSource).join(Suite, DBSource.suites)
180 q = q.filter(or_(Suite.suite_name == suite, Suite.codename == suite))
181 ret = []
182 for p in q:
183 ret.append({"source": p.source, "version": p.version})
185 s.close()
187 bottle.response.content_type = "application/json; charset=UTF-8"
188 return json.dumps(ret)
191QueryRegister().register_path("/sources_in_suite", sources_in_suite)
194@bottle.route("/all_sources")
195def all_sources() -> str:
196 """
197 Returns all source packages and their versions known to the archive
198 (this includes NEW).
200 :return: List of dictionaries made out of
201 - source
202 - version
203 """
205 s = DBConn().session()
206 q = s.query(DBSource)
207 ret = []
208 for p in q:
209 ret.append({"source": p.source, "version": p.version})
211 s.close()
213 bottle.response.content_type = "application/json; charset=UTF-8"
214 return json.dumps(ret)
217QueryRegister().register_path("/all_sources", all_sources)
220@bottle.route("/source/by_metadata/<key>")
221def source_by_metadata(key: Optional[str] = None) -> str:
222 """
224 Finds all Debian source packages which have the specified metadata set.
226 E.g., to find out the Maintainer of all source packages, query
227 /source/by_metadata/Maintainer.
229 :param key: Metadata key to search for.
230 :return: A list of dictionaries of
231 - source
232 - metadata value
233 """
235 if not key:
236 return bottle.HTTPError(503, "Metadata key not specified.")
238 s = DBConn().session()
239 q = s.query(DBSource.source, SourceMetadata.value)
240 q = q.join(SourceMetadata).join(MetadataKey)
241 q = q.filter(MetadataKey.key == key)
242 ret = []
243 for p in q:
244 ret.append({"source": p.source, "metadata_value": p.value})
245 s.close()
247 bottle.response.content_type = "application/json; charset=UTF-8"
248 return json.dumps(ret)
251QueryRegister().register_path("/source/by_metadata", source_by_metadata)