mirror of
https://github.com/minetest/contentdb.git
synced 2025-01-08 22:17:34 +01:00
Merge pull request #78 from minetest/dev
Add meta packages, remove current dependencies
This commit is contained in:
commit
d046de8057
@ -33,7 +33,7 @@ the current session:
|
||||
|
||||
If you need to, reset the db like so:
|
||||
|
||||
python3 setup.py -d
|
||||
python3 setup.py -t
|
||||
|
||||
Then run the server:
|
||||
|
||||
@ -43,6 +43,12 @@ Then view in your web browser: http://localhost:5000/
|
||||
|
||||
## How-tos
|
||||
|
||||
### Start celery worker
|
||||
|
||||
```sh
|
||||
FLASK_CONFIG=../config.cfg celery -A app.tasks.celery worker
|
||||
```
|
||||
|
||||
### Create migration
|
||||
|
||||
```sh
|
||||
|
186
app/models.py
186
app/models.py
@ -219,8 +219,7 @@ class PackagePropertyKey(enum.Enum):
|
||||
type = "Type"
|
||||
license = "License"
|
||||
tags = "Tags"
|
||||
harddeps = "Hard Dependencies"
|
||||
softdeps = "Soft Dependencies"
|
||||
provides = "Provides"
|
||||
repo = "Repository"
|
||||
website = "Website"
|
||||
issueTracker = "Issue Tracker"
|
||||
@ -229,26 +228,88 @@ class PackagePropertyKey(enum.Enum):
|
||||
def convert(self, value):
|
||||
if self == PackagePropertyKey.tags:
|
||||
return ",".join([t.title for t in value])
|
||||
elif self == PackagePropertyKey.harddeps or self == PackagePropertyKey.softdeps:
|
||||
return ",".join([t.author.username + "/" + t.name for t in value])
|
||||
|
||||
elif self == PackagePropertyKey.provides:
|
||||
return ",".join([t.name for t in value])
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
provides = db.Table("provides",
|
||||
db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True),
|
||||
db.Column("metapackage_id", db.Integer, db.ForeignKey("meta_package.id"), primary_key=True)
|
||||
)
|
||||
|
||||
tags = db.Table("tags",
|
||||
db.Column("tag_id", db.Integer, db.ForeignKey("tag.id"), primary_key=True),
|
||||
db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
|
||||
)
|
||||
|
||||
harddeps = db.Table("harddeps",
|
||||
db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True),
|
||||
db.Column("dependency_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
|
||||
)
|
||||
class Dependency(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
depender_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
|
||||
package_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
|
||||
package = db.relationship("Package", foreign_keys=[package_id])
|
||||
meta_package_id = db.Column(db.Integer, db.ForeignKey("meta_package.id"), nullable=True)
|
||||
optional = db.Column(db.Boolean, nullable=False, default=False)
|
||||
__table_args__ = (db.UniqueConstraint('depender_id', 'package_id', 'meta_package_id', name='_dependency_uc'), )
|
||||
|
||||
def __init__(self, depender=None, package=None, meta=None):
|
||||
if depender is None:
|
||||
return
|
||||
|
||||
self.depender = depender
|
||||
|
||||
packageProvided = package is not None
|
||||
metaProvided = meta is not None
|
||||
|
||||
if packageProvided and not metaProvided:
|
||||
self.package = package
|
||||
elif metaProvided and not packageProvided:
|
||||
self.meta_package = meta
|
||||
else:
|
||||
raise Exception("Either meta or package must be given, but not both!")
|
||||
|
||||
def __str__(self):
|
||||
if self.package is not None:
|
||||
return self.package.author.username + "/" + self.package.name
|
||||
elif self.meta_package is not None:
|
||||
return self.meta_package.name
|
||||
else:
|
||||
raise Exception("Meta and package are both none!")
|
||||
|
||||
@staticmethod
|
||||
def SpecToList(depender, spec, cache={}):
|
||||
retval = []
|
||||
arr = spec.split(",")
|
||||
|
||||
import re
|
||||
pattern1 = re.compile("^([a-z0-9_]+)$")
|
||||
pattern2 = re.compile("^([A-Za-z0-9_]+)/([a-z0-9_]+)$")
|
||||
|
||||
for x in arr:
|
||||
x = x.strip()
|
||||
if x == "":
|
||||
continue
|
||||
|
||||
if pattern1.match(x):
|
||||
meta = MetaPackage.GetOrCreate(x, cache)
|
||||
retval.append(Dependency(depender, meta=meta))
|
||||
else:
|
||||
m = pattern2.match(x)
|
||||
username = m.group(1)
|
||||
name = m.group(2)
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is None:
|
||||
raise Exception("Unable to find user " + username)
|
||||
|
||||
package = Package.query.filter_by(author=user, name=name).first()
|
||||
if package is None:
|
||||
raise Exception("Unable to find package " + name + " by " + username)
|
||||
|
||||
retval.append(Dependency(depender, package=package))
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
softdeps = db.Table("softdeps",
|
||||
db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True),
|
||||
db.Column("dependency_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
|
||||
)
|
||||
|
||||
class Package(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
@ -273,20 +334,13 @@ class Package(db.Model):
|
||||
issueTracker = db.Column(db.String(200), nullable=True)
|
||||
forums = db.Column(db.Integer, nullable=True)
|
||||
|
||||
tags = db.relationship("Tag", secondary=tags, lazy="subquery",
|
||||
provides = db.relationship("MetaPackage", secondary=provides, lazy="subquery",
|
||||
backref=db.backref("packages", lazy=True))
|
||||
|
||||
harddeps = db.relationship("Package",
|
||||
secondary=harddeps,
|
||||
primaryjoin=id==harddeps.c.package_id,
|
||||
secondaryjoin=id==harddeps.c.dependency_id,
|
||||
backref="dependents")
|
||||
dependencies = db.relationship("Dependency", backref="depender", lazy="dynamic", foreign_keys=[Dependency.depender_id])
|
||||
|
||||
softdeps = db.relationship("Package",
|
||||
secondary=softdeps,
|
||||
primaryjoin=id==softdeps.c.package_id,
|
||||
secondaryjoin=id==softdeps.c.dependency_id,
|
||||
backref="softdependents")
|
||||
tags = db.relationship("Tag", secondary=tags, lazy="subquery",
|
||||
backref=db.backref("packages", lazy=True))
|
||||
|
||||
releases = db.relationship("PackageRelease", backref="package",
|
||||
lazy="dynamic", order_by=db.desc("package_release_releaseDate"))
|
||||
@ -418,6 +472,54 @@ class Package(db.Model):
|
||||
else:
|
||||
raise Exception("Permission {} is not related to packages".format(perm.name))
|
||||
|
||||
class MetaPackage(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), unique=True, nullable=False)
|
||||
dependencies = db.relationship("Dependency", backref="meta_package", lazy="dynamic")
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def ListToSpec(list):
|
||||
return ",".join([str(x) for x in list])
|
||||
|
||||
@staticmethod
|
||||
def GetOrCreate(name, cache={}):
|
||||
mp = cache.get(name)
|
||||
if mp is None:
|
||||
mp = MetaPackage.query.filter_by(name=name).first()
|
||||
|
||||
if mp is None:
|
||||
mp = MetaPackage(name)
|
||||
db.session.add(mp)
|
||||
|
||||
cache[name] = mp
|
||||
return mp
|
||||
|
||||
@staticmethod
|
||||
def SpecToList(spec, cache={}):
|
||||
retval = []
|
||||
arr = spec.split(",")
|
||||
|
||||
import re
|
||||
pattern = re.compile("^([a-z0-9_]+)$")
|
||||
|
||||
for x in arr:
|
||||
x = x.strip()
|
||||
if x == "":
|
||||
continue
|
||||
|
||||
if not pattern.match(x):
|
||||
continue
|
||||
|
||||
retval.append(MetaPackage.GetOrCreate(x, cache))
|
||||
|
||||
return retval
|
||||
|
||||
class Tag(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), unique=True, nullable=False)
|
||||
@ -555,42 +657,6 @@ class EditRequestChange(db.Model):
|
||||
tag = Tag.query.filter_by(title=tagTitle.strip()).first()
|
||||
package.tags.append(tag)
|
||||
|
||||
elif self.key == PackagePropertyKey.harddeps:
|
||||
package.harddeps.clear()
|
||||
for pair in self.newValue.split(","):
|
||||
key, value = pair.split("/")
|
||||
if key is None or value is None:
|
||||
continue
|
||||
|
||||
user = User.query.filter_by(username=key).first()
|
||||
if user is None:
|
||||
continue
|
||||
|
||||
dep = Package.query.filter_by(author=user, name=value, soft_deleted=False).first()
|
||||
if dep is None:
|
||||
continue
|
||||
|
||||
package.harddeps.append(dep)
|
||||
|
||||
elif self.key == PackagePropertyKey.softdeps:
|
||||
package.softdeps.clear()
|
||||
for pair in self.newValue.split(","):
|
||||
key, value = pair.split("/")
|
||||
if key is None or value is None:
|
||||
continue
|
||||
|
||||
user = User.query.filter_by(username=key).first()
|
||||
if user is None:
|
||||
raise Exception("No such user!")
|
||||
continue
|
||||
|
||||
dep = Package.query.filter_by(author=user, name=value).first()
|
||||
if dep is None:
|
||||
raise Exception("No such package!")
|
||||
continue
|
||||
|
||||
package.softdeps.append(dep)
|
||||
|
||||
else:
|
||||
setattr(package, self.key.name, self.newValue)
|
||||
|
||||
|
@ -26,13 +26,25 @@ $(function() {
|
||||
$(".pkg_wiz_2").show()
|
||||
$(".pkg_repo").hide()
|
||||
|
||||
function setSpecial(id, value) {
|
||||
if (value != "") {
|
||||
var ele = $(id);
|
||||
ele.val(value);
|
||||
ele.trigger("change")
|
||||
}
|
||||
}
|
||||
|
||||
performTask("/tasks/getmeta/new/?url=" + encodeURI(repoURL)).then(function(result) {
|
||||
$("#name").val(result.name || "")
|
||||
setSpecial("#provides_str", result.name || "")
|
||||
$("#title").val(result.title || "")
|
||||
$("#repo").val(result.repo || repoURL)
|
||||
$("#issueTracker").val(result.issueTracker || "")
|
||||
$("#desc").val(result.description || "")
|
||||
$("#shortDesc").val(result.short_description || "")
|
||||
setSpecial("#harddep_str", result.depends || "")
|
||||
setSpecial("#softdep_str", result.optional_depends || "")
|
||||
$("#shortDesc").val(result.short_description || "")
|
||||
if (result.forumId) {
|
||||
$("#forums").val(result.forumId)
|
||||
}
|
||||
|
11
app/public/static/package_edit.js
Normal file
11
app/public/static/package_edit.js
Normal file
@ -0,0 +1,11 @@
|
||||
// @author rubenwardy
|
||||
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
|
||||
|
||||
$(function() {
|
||||
$("#type").change(function() {
|
||||
$(".not_mod, .not_game, .not_txp").show()
|
||||
$(".not_" + this.value.toLowerCase()).hide()
|
||||
})
|
||||
$(".not_mod, .not_game, .not_txp").show()
|
||||
$(".not_" + $("#type").val().toLowerCase()).hide()
|
||||
})
|
@ -5,7 +5,7 @@
|
||||
* https://petprojects.googlecode.com/svn/trunk/GPL-LICENSE.txt
|
||||
*/
|
||||
(function($) {
|
||||
$.fn.tagSelector = function(source, name, select) {
|
||||
$.fn.selectSelector = function(source, name, select) {
|
||||
return this.each(function() {
|
||||
var selector = $(this),
|
||||
input = $('input[type=text]', this);
|
||||
@ -80,15 +80,136 @@
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.csvSelector = function(source, name, result, allowSlash) {
|
||||
return this.each(function() {
|
||||
var selector = $(this),
|
||||
input = $('input[type=text]', this);
|
||||
|
||||
var selected = [];
|
||||
var lookup = {};
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
lookup[source[i].id] = source[i];
|
||||
}
|
||||
|
||||
selector.click(function() { input.focus(); })
|
||||
.delegate('.tag a', 'click', function() {
|
||||
var id = $(this).parent().data("id");
|
||||
for (var i = 0; i < selected.length; i++) {
|
||||
if (selected[i] == id) {
|
||||
selected.splice(i, 1);
|
||||
}
|
||||
}
|
||||
recreate();
|
||||
});
|
||||
|
||||
function selectItem(id) {
|
||||
for (var i = 0; i < selected.length; i++) {
|
||||
if (selected[i] == id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
selected.push(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
function addTag(id, value) {
|
||||
var tag = $('<span class="tag"/>')
|
||||
.text(value)
|
||||
.data("id", id)
|
||||
.append(' <a>x</a>')
|
||||
.insertBefore(input);
|
||||
|
||||
input.attr("placeholder", null);
|
||||
}
|
||||
|
||||
function recreate() {
|
||||
selector.find("span").remove();
|
||||
for (var i = 0; i < selected.length; i++) {
|
||||
var value = lookup[selected[i]] || { value: selected[i] };
|
||||
addTag(selected[i], value.value);
|
||||
}
|
||||
result.val(selected.join(","))
|
||||
}
|
||||
|
||||
function readFromResult() {
|
||||
selected = [];
|
||||
var selected_raw = result.val().split(",");
|
||||
for (var i = 0; i < selected_raw.length; i++) {
|
||||
var raw = selected_raw[i].trim();
|
||||
if (lookup[raw] || raw.match(/^([a-z0-9_]+)$/)) {
|
||||
selected.push(raw);
|
||||
}
|
||||
}
|
||||
|
||||
recreate();
|
||||
}
|
||||
readFromResult();
|
||||
|
||||
result.change(readFromResult);
|
||||
|
||||
input.keydown(function(e) {
|
||||
if (e.keyCode === $.ui.keyCode.TAB && $(this).data('ui-autocomplete').menu.active)
|
||||
e.preventDefault();
|
||||
else if (e.keyCode === $.ui.keyCode.COMMA) {
|
||||
var item = input.val();
|
||||
if (item.length == 0) {
|
||||
input.data("ui-autocomplete").search("");
|
||||
} else if (item.match(/^([a-z0-9_]+)$/)) {
|
||||
selectItem(item);
|
||||
recreate();
|
||||
input.val("");
|
||||
} else {
|
||||
alert("Only lowercase alphanumeric and number names allowed.");
|
||||
}
|
||||
e.preventDefault();
|
||||
return true;
|
||||
} else if (e.keyCode === $.ui.keyCode.BACKSPACE) {
|
||||
if (input.val() == "") {
|
||||
var item = selected[selected.length - 1];
|
||||
selected.splice(selected.length - 1, 1);
|
||||
recreate();
|
||||
if (!(item.indexOf("/") > 0))
|
||||
input.val(item);
|
||||
e.preventDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
.autocomplete({
|
||||
minLength: 0,
|
||||
source: source,
|
||||
select: function(event, ui) {
|
||||
selectItem(ui.item.id);
|
||||
recreate();
|
||||
input.val("");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
input.data('ui-autocomplete')._renderItem = function(ul, item) {
|
||||
return $('<li/>')
|
||||
.data('item.autocomplete', item)
|
||||
.append($('<a/>').text(item.toString()))
|
||||
.appendTo(ul);
|
||||
};
|
||||
|
||||
input.data('ui-autocomplete')._resizeMenu = function(ul, item) {
|
||||
var ul = this.menu.element;
|
||||
ul.outerWidth(Math.max(
|
||||
ul.width('').outerWidth(),
|
||||
selector.outerWidth()
|
||||
));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(".multichoice_selector").each(function() {
|
||||
var ele = $(this);
|
||||
var sel = ele.parent().find("select");
|
||||
console.log(sel.attr("name"));
|
||||
sel.css("display", "none");
|
||||
sel.hide();
|
||||
|
||||
var options = [];
|
||||
|
||||
sel.find("option").each(function() {
|
||||
var text = $(this).text();
|
||||
options.push({
|
||||
@ -100,7 +221,19 @@
|
||||
});
|
||||
|
||||
console.log(options);
|
||||
ele.tagSelector(options, sel.attr("name"), sel);
|
||||
})
|
||||
ele.selectSelector(options, sel.attr("name"), sel);
|
||||
});
|
||||
|
||||
$(".metapackage_selector").each(function() {
|
||||
var input = $(this).parent().children("input[type='text']");
|
||||
input.hide();
|
||||
$(this).csvSelector(meta_packages, input.attr("name"), input);
|
||||
});
|
||||
|
||||
$(".deps_selector").each(function() {
|
||||
var input = $(this).parent().children("input[type='text']");
|
||||
input.hide();
|
||||
$(this).csvSelector(all_packages, input.attr("name"), input);
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
||||
|
@ -87,7 +87,7 @@ a:hover {
|
||||
}
|
||||
|
||||
.button, .buttonset li a, input[type=submit], input[type=text],
|
||||
input[type=password], textarea, select, .multichoice_selector {
|
||||
input[type=password], textarea, select, .bulletselector {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 0.4em 1em;
|
||||
@ -99,7 +99,7 @@ a:hover {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
input[type=text], input[type=password], textarea, select, .multichoice_selector {
|
||||
input[type=text], input[type=password], textarea, select, .bulletselector {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@ -147,13 +147,13 @@ select:not([multiple]) {
|
||||
padding: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.form-group input, .form-group textarea, .form-group .multichoice_selector {
|
||||
.form-group input, .form-group textarea, .form-group .bulletselector {
|
||||
display: block;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.box .form-group input, .box .form-group textarea, .form-group .multichoice_selector {
|
||||
.box .form-group input, .box .form-group textarea, .form-group .bulletselector {
|
||||
min-width: 95%;
|
||||
max-width: 95%;
|
||||
}
|
||||
@ -197,7 +197,7 @@ select:not([multiple]) {
|
||||
}
|
||||
|
||||
|
||||
.multichoice_selector input {
|
||||
.bulletselector input {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
@ -211,7 +211,7 @@ select:not([multiple]) {
|
||||
white-space: nowrap;
|
||||
background: transparent;
|
||||
}
|
||||
.multichoice_selector .tag {
|
||||
.bulletselector .tag {
|
||||
background: #375D81;
|
||||
border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
@ -223,11 +223,11 @@ select:not([multiple]) {
|
||||
margin-bottom: 0.3em;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.multichoice_selector .tag a {
|
||||
.bulletselector .tag a {
|
||||
color: #FFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
.multichoice_selector .tag a:hover {
|
||||
.bulletselector .tag a:hover {
|
||||
color: #0099CC;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ class GithubURLMaker:
|
||||
def getDescURL(self):
|
||||
return self.baseUrl + "/description.txt"
|
||||
|
||||
def getDependsURL(self):
|
||||
return self.baseUrl + "/depends.txt"
|
||||
|
||||
def getScreenshotURL(self):
|
||||
return self.baseUrl + "/screenshot.png"
|
||||
|
||||
@ -161,7 +164,7 @@ def getMeta(urlstr, author):
|
||||
try:
|
||||
contents = urllib.request.urlopen(urlmaker.getModConfURL()).read().decode("utf-8")
|
||||
conf = parseConf(contents)
|
||||
for key in ["name", "description", "title"]:
|
||||
for key in ["name", "description", "title", "depends", "optional_depends"]:
|
||||
try:
|
||||
result[key] = conf[key]
|
||||
except KeyError:
|
||||
@ -179,12 +182,35 @@ def getMeta(urlstr, author):
|
||||
except HTTPError:
|
||||
print("description.txt does not exist!")
|
||||
|
||||
import re
|
||||
pattern = re.compile("^([a-z0-9_]+)\??$")
|
||||
if not "depends" in result and not "optional_depends" in result:
|
||||
try:
|
||||
contents = urllib.request.urlopen(urlmaker.getDependsURL()).read().decode("utf-8")
|
||||
soft = []
|
||||
hard = []
|
||||
for line in contents.split("\n"):
|
||||
line = line.strip()
|
||||
if pattern.match(line):
|
||||
if line[len(line) - 1] == "?":
|
||||
soft.append( line[:-1])
|
||||
else:
|
||||
hard.append(line)
|
||||
|
||||
result["depends"] = ",".join(hard)
|
||||
result["optional_depends"] = ",".join(soft)
|
||||
|
||||
|
||||
except HTTPError:
|
||||
print("depends.txt does not exist!")
|
||||
|
||||
if "description" in result:
|
||||
desc = result["description"]
|
||||
idx = desc.find(".") + 1
|
||||
cutIdx = min(len(desc), 200 if idx < 5 else idx)
|
||||
result["short_description"] = desc[:cutIdx]
|
||||
|
||||
|
||||
info = findModInfo(author, result.get("name"), result["repo"])
|
||||
if info is not None:
|
||||
result["forumId"] = info.get("topicId")
|
||||
@ -264,3 +290,94 @@ def importRepoScreenshot(id):
|
||||
print("screenshot.png does not exist")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def getDepends(package):
|
||||
url = urlparse(package.repo)
|
||||
urlmaker = None
|
||||
if url.netloc == "github.com":
|
||||
urlmaker = GithubURLMaker(url)
|
||||
else:
|
||||
raise TaskError("Unsupported repo")
|
||||
|
||||
result = {}
|
||||
if urlmaker.isValid():
|
||||
#
|
||||
# Try getting depends on mod.conf
|
||||
#
|
||||
try:
|
||||
contents = urllib.request.urlopen(urlmaker.getModConfURL()).read().decode("utf-8")
|
||||
conf = parseConf(contents)
|
||||
for key in ["depends", "optional_depends"]:
|
||||
try:
|
||||
result[key] = conf[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
except HTTPError:
|
||||
print("mod.conf does not exist")
|
||||
|
||||
if "depends" in result or "optional_depends" in result:
|
||||
return result
|
||||
|
||||
|
||||
#
|
||||
# Try depends.txt
|
||||
#
|
||||
import re
|
||||
pattern = re.compile("^([a-z0-9_]+)\??$")
|
||||
try:
|
||||
contents = urllib.request.urlopen(urlmaker.getDependsURL()).read().decode("utf-8")
|
||||
soft = []
|
||||
hard = []
|
||||
for line in contents.split("\n"):
|
||||
line = line.strip()
|
||||
if pattern.match(line):
|
||||
if line[len(line) - 1] == "?":
|
||||
soft.append( line[:-1])
|
||||
else:
|
||||
hard.append(line)
|
||||
|
||||
result["depends"] = ",".join(hard)
|
||||
result["optional_depends"] = ",".join(soft)
|
||||
except HTTPError:
|
||||
print("depends.txt does not exist")
|
||||
|
||||
return result
|
||||
|
||||
else:
|
||||
print(TaskError("non-github depends detector not implemented yet!"))
|
||||
return {}
|
||||
|
||||
|
||||
def importDependencies(package, mpackage_cache):
|
||||
if Dependency.query.filter_by(depender=package).count() != 0:
|
||||
return
|
||||
|
||||
result = getDepends(package)
|
||||
|
||||
if "depends" in result:
|
||||
deps = Dependency.SpecToList(package, result["depends"], mpackage_cache)
|
||||
print("{} hard: {}".format(len(deps), result["depends"]))
|
||||
for dep in deps:
|
||||
dep.optional = False
|
||||
db.session.add(dep)
|
||||
|
||||
if "optional_depends" in result:
|
||||
deps = Dependency.SpecToList(package, result["optional_depends"], mpackage_cache)
|
||||
print("{} soft: {}".format(len(deps), result["optional_depends"]))
|
||||
for dep in deps:
|
||||
dep.optional = True
|
||||
db.session.add(dep)
|
||||
|
||||
@celery.task()
|
||||
def importAllDependencies():
|
||||
Dependency.query.delete()
|
||||
mpackage_cache = {}
|
||||
packages = Package.query.filter_by(type=PackageType.MOD).all()
|
||||
for i, p in enumerate(packages):
|
||||
print("============= {} ({}/{}) =============".format(p.name, i, len(packages)))
|
||||
importDependencies(p, mpackage_cache)
|
||||
|
||||
db.session.commit()
|
||||
|
@ -17,8 +17,9 @@
|
||||
<form method="post" action="" class="box-body">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<select name="action">
|
||||
<option value="importusers" selected>Create users from mod list</option>
|
||||
<option value="importusers">Create users from mod list</option>
|
||||
<option value="importscreenshots">Import screenshots from VCS</option>
|
||||
<option value="importdepends" selected>Import dependencies from downloads</option>
|
||||
</select>
|
||||
<input type="submit" value="Perform" />
|
||||
</form>
|
||||
|
@ -26,7 +26,7 @@
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
|
||||
{% endif %}
|
||||
<div class="multichoice_selector">
|
||||
<div class="multichoice_selector bulletselector">
|
||||
<input type="text" placeholder="Start typing to see suggestions">
|
||||
<div class="clearboth"></div>
|
||||
</div>
|
||||
@ -39,6 +39,44 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_mpackage_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
|
||||
{% if field.type != 'HiddenField' and label_visible %}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
|
||||
{% endif %}
|
||||
<div class="metapackage_selector bulletselector">
|
||||
<input type="text" placeholder="Comma-seperated values">
|
||||
<div class="clearboth"></div>
|
||||
</div>
|
||||
{{ field(class_='form-control', **kwargs) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_deps_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
|
||||
{% if field.type != 'HiddenField' and label_visible %}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
|
||||
{% endif %}
|
||||
<div class="deps_selector bulletselector">
|
||||
<input type="text" placeholder="Comma-seperated values">
|
||||
<div class="clearboth"></div>
|
||||
</div>
|
||||
{{ field(class_='form-control', **kwargs) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_checkbox_field(field, label=None) -%}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<div class="checkbox">
|
||||
|
15
app/templates/meta/list.html
Normal file
15
app/templates/meta/list.html
Normal file
@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Meta Packages
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for meta in mpackages %}
|
||||
<li><a href="{{ url_for('meta_package_page', name=meta.name) }}">{{ meta.name }}</a> ({{ meta.packages | count }} packages)</li>
|
||||
{% else %}
|
||||
<li><i>No meta packages found.</i></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
12
app/templates/meta/view.html
Normal file
12
app/templates/meta/view.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Packages providing '{{ mpackage.name }}''
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Packages providing '{{ mpackage.name }}''</h1>
|
||||
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
{{ render_pkggrid(mpackage.packages) }}
|
||||
{% endblock %}
|
@ -8,23 +8,65 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Create Package</h2>
|
||||
<h1>Create Package</h1>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field %}
|
||||
<script>
|
||||
meta_packages = [
|
||||
{% for m in mpackages %}
|
||||
{# This is safe as name can only contain `[a-z0-9_]` #}
|
||||
{
|
||||
id: "{{ m.name }}",
|
||||
value: "{{ m.name }}",
|
||||
toString: function() { return "{{ m.name }}"; },
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
function escape(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
all_packages = meta_packages.slice();
|
||||
|
||||
{% for p in packages %}
|
||||
all_packages.push({
|
||||
id: "{{ p.author.username }}/{{ p.name }}",
|
||||
value: escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }}),
|
||||
toString: function() { return escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }} + " only"); },
|
||||
});
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field, render_mpackage_field, render_deps_field %}
|
||||
{{ form_includes() }}
|
||||
|
||||
<form method="POST" action="" class="tableform">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<h2 class="pkg_meta">Package</h2>
|
||||
|
||||
{{ render_field(form.type, class_="pkg_meta") }}
|
||||
{{ render_field(form.name, class_="pkg_meta") }}
|
||||
{{ render_field(form.title, class_="pkg_meta") }}
|
||||
{{ render_field(form.shortDesc, class_="pkg_meta") }}
|
||||
{{ render_field(form.desc, class_="pkg_meta") }}
|
||||
{{ render_field(form.type, class_="pkg_meta") }}
|
||||
{{ render_field(form.license, class_="pkg_meta") }}
|
||||
{{ render_multiselect_field(form.tags, class_="pkg_meta") }}
|
||||
{{ render_multiselect_field(form.harddeps, class_="pkg_meta") }}
|
||||
{{ render_multiselect_field(form.softdeps, class_="pkg_meta") }}
|
||||
{{ render_field(form.license, class_="pkg_meta") }}
|
||||
|
||||
<div class="pkg_meta">
|
||||
<h2 class="not_txp">Dependency Info</h2>
|
||||
|
||||
{{ render_mpackage_field(form.provides_str, class_="not_txp", placeholder="Comma separated list") }}
|
||||
{{ render_deps_field(form.harddep_str, class_="not_txp not_game", placeholder="Comma separated list") }}
|
||||
{{ render_deps_field(form.softdep_str, class_="not_txp not_game", placeholder="Comma separated list") }}
|
||||
</div>
|
||||
|
||||
<h2 class="pkg_meta">Repository and Links</h2>
|
||||
|
||||
<div class="pkg_wiz_1">
|
||||
<p>Enter the repo URL for the package.
|
||||
@ -60,4 +102,5 @@
|
||||
</div>
|
||||
</noscript>
|
||||
{% endif %}
|
||||
<script src="/static/package_edit.js"></script>
|
||||
{% endblock %}
|
||||
|
@ -18,8 +18,6 @@
|
||||
{{ render_field(form.type) }}
|
||||
{{ render_field(form.license) }}
|
||||
{{ render_multiselect_field(form.tags) }}
|
||||
{{ render_multiselect_field(form.harddeps) }}
|
||||
{{ render_multiselect_field(form.softdeps) }}
|
||||
{{ render_field(form.repo) }}
|
||||
{{ render_field(form.website) }}
|
||||
{{ render_field(form.issueTracker) }}
|
||||
|
@ -67,6 +67,15 @@
|
||||
<td>Name</td>
|
||||
<td>{{ package.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Provides</td>
|
||||
<td>{% for meta in package.provides %}
|
||||
<a href="{{ url_for('meta_package_page', name=meta.name) }}">{{ meta.name }}</a>
|
||||
{%- if not loop.last %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endfor %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td class="{{ package.author.rank }}">
|
||||
@ -153,23 +162,29 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<table class="table-topalign">
|
||||
<!-- <table class="table-topalign">
|
||||
<tr>
|
||||
<td>
|
||||
<td> -->
|
||||
<h3>Dependencies</h3>
|
||||
<ul>
|
||||
{% for p in package.harddeps %}
|
||||
<li><a href="{{ p.getDetailsURL() }}">{{ p.title }}</a> by {{ p.author.display_name }}</li>
|
||||
{% for dep in package.dependencies %}
|
||||
<li>
|
||||
{%- if dep.package %}
|
||||
<a href="{{ dep.package.getDetailsURL() }}">{{ dep.package.title }}</a> by {{ dep.package.author.display_name }}
|
||||
{% elif dep.meta_package %}
|
||||
<a href="{{ url_for('meta_package_page', name=dep.meta_package.name) }}">{{ dep.meta_package.name }}</a>
|
||||
{% else %}
|
||||
{{ "Excepted package or meta_package in dep!" | throw }}
|
||||
{% endif %}
|
||||
{% if dep.optional %}
|
||||
[optional]
|
||||
{% endif %}
|
||||
</li>
|
||||
{% else %}
|
||||
{% if not package.softdeps %}
|
||||
<li>No dependencies.</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for p in package.softdeps %}
|
||||
<li><a href="{{ p.getDetailsURL() }}">{{ p.title }}</a> by {{ p.author.display_name }} [optional]</li>
|
||||
<li><i>No dependencies</i></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
<!-- </td>
|
||||
<td>
|
||||
<h3>Required by</h3>
|
||||
<ul>
|
||||
@ -186,7 +201,7 @@
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table> -->
|
||||
|
||||
{% if current_user.is_authenticated or requests %}
|
||||
<h3>Edit Requests</h3>
|
||||
|
@ -27,6 +27,10 @@ from werkzeug.contrib.cache import SimpleCache
|
||||
from urllib.parse import urlparse
|
||||
cache = SimpleCache()
|
||||
|
||||
@app.template_filter()
|
||||
def throw(err):
|
||||
raise Exception(err)
|
||||
|
||||
@app.template_filter()
|
||||
def domain(url):
|
||||
return urlparse(url).netloc
|
||||
@ -43,7 +47,7 @@ def home_page():
|
||||
packages = query.order_by(db.desc(Package.created_at)).limit(15).all()
|
||||
return render_template("index.html", packages=packages, count=count)
|
||||
|
||||
from . import users, githublogin, packages, sass, tasks, admin, notifications, tagseditor
|
||||
from . import users, githublogin, packages, sass, tasks, admin, notifications, tagseditor, meta
|
||||
|
||||
@menu.register_menu(app, ".help", "Help", order=19, endpoint_arguments_constructor=lambda: { 'path': 'help' })
|
||||
@app.route('/<path:path>/')
|
||||
|
@ -20,7 +20,7 @@ from flask_user import *
|
||||
from flask.ext import menu
|
||||
from app import app
|
||||
from app.models import *
|
||||
from app.tasks.importtasks import importRepoScreenshot
|
||||
from app.tasks.importtasks import importRepoScreenshot, importAllDependencies
|
||||
from app.tasks.forumtasks import importUsersFromModList
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import *
|
||||
@ -52,6 +52,9 @@ def admin_page():
|
||||
package.soft_deleted = False
|
||||
db.session.commit()
|
||||
return redirect(url_for("admin_page"))
|
||||
elif action == "importdepends":
|
||||
task = importAllDependencies.delay()
|
||||
return redirect(url_for("check_task", id=task.id, r=url_for("admin_page")))
|
||||
else:
|
||||
flash("Unknown action: " + action, "error")
|
||||
|
||||
|
34
app/views/meta.py
Normal file
34
app/views/meta.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Content DB
|
||||
# Copyright (C) 2018 rubenwardy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from flask import *
|
||||
from flask_user import *
|
||||
from app import app
|
||||
from app.models import *
|
||||
|
||||
@app.route("/metapackages/")
|
||||
def meta_package_list_page():
|
||||
mpackages = MetaPackage.query.order_by(db.desc(MetaPackage.name)).all()
|
||||
return render_template("meta/list.html", mpackages=mpackages)
|
||||
|
||||
@app.route("/metapackages/<name>/")
|
||||
def meta_package_page(name):
|
||||
mpackage = MetaPackage.query.filter_by(name=name).first()
|
||||
if mpackage is None:
|
||||
abort(404)
|
||||
|
||||
return render_template("meta/view.html", mpackage=mpackage)
|
@ -102,13 +102,14 @@ def package_download_page(package):
|
||||
class PackageForm(FlaskForm):
|
||||
name = StringField("Name", [InputRequired(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
|
||||
title = StringField("Title", [InputRequired(), Length(3, 50)])
|
||||
shortDesc = StringField("Short Description", [InputRequired(), Length(1,200)])
|
||||
desc = TextAreaField("Long Description", [Optional(), Length(0,10000)])
|
||||
shortDesc = StringField("Short Description (Plaintext)", [InputRequired(), Length(1,200)])
|
||||
desc = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
|
||||
type = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
|
||||
license = QuerySelectField("License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
provides_str = StringField("Provides (mods included in package)", [Optional(), Length(0,1000)])
|
||||
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
|
||||
harddeps = QuerySelectMultipleField('Dependencies', query_factory=lambda: Package.query.filter_by(soft_deleted=False,approved=True).join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
|
||||
softdeps = QuerySelectMultipleField('Soft Dependencies', query_factory=lambda: Package.query.filter_by(soft_deleted=False,approved=True).join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
|
||||
harddep_str = StringField("Hard Dependencies", [Optional(), Length(0,1000)])
|
||||
softdep_str = StringField("Soft Dependencies", [Optional(), Length(0,1000)])
|
||||
repo = StringField("Repo URL", [Optional(), URL()])
|
||||
website = StringField("Website URL", [Optional(), URL()])
|
||||
issueTracker = StringField("Issue Tracker URL", [Optional(), URL()])
|
||||
@ -146,6 +147,12 @@ def create_edit_package_page(author=None, name=None):
|
||||
form = PackageForm(formdata=request.form, obj=package)
|
||||
|
||||
# Initial form class from post data and default data
|
||||
if request.method == "GET" and package is not None:
|
||||
deps = package.dependencies
|
||||
form.harddep_str.data = ",".join([str(x) for x in deps if not x.optional])
|
||||
form.softdep_str.data = ",".join([str(x) for x in deps if x.optional])
|
||||
form.provides_str.data = MetaPackage.ListToSpec(package.provides)
|
||||
|
||||
if request.method == "POST" and form.validate():
|
||||
wasNew = False
|
||||
if not package:
|
||||
@ -166,6 +173,27 @@ def create_edit_package_page(author=None, name=None):
|
||||
|
||||
form.populate_obj(package) # copy to row
|
||||
|
||||
mpackage_cache = {}
|
||||
package.provides.clear()
|
||||
mpackages = MetaPackage.SpecToList(form.provides_str.data, mpackage_cache)
|
||||
for m in mpackages:
|
||||
package.provides.append(m)
|
||||
|
||||
Dependency.query.filter_by(depender=package).delete()
|
||||
deps = Dependency.SpecToList(package, form.harddep_str.data, mpackage_cache)
|
||||
for dep in deps:
|
||||
dep.optional = False
|
||||
db.session.add(dep)
|
||||
|
||||
deps = Dependency.SpecToList(package, form.softdep_str.data, mpackage_cache)
|
||||
for dep in deps:
|
||||
dep.optional = True
|
||||
db.session.add(dep)
|
||||
|
||||
if wasNew and package.type == PackageType.MOD and not package.name in mpackage_cache:
|
||||
m = MetaPackage.GetOrCreate(package.name, mpackage_cache)
|
||||
package.provides.append(m)
|
||||
|
||||
package.tags.clear()
|
||||
for tag in form.tags.raw_data:
|
||||
package.tags.append(Tag.query.get(tag))
|
||||
@ -178,9 +206,15 @@ def create_edit_package_page(author=None, name=None):
|
||||
|
||||
return redirect(package.getDetailsURL())
|
||||
|
||||
package_query = Package.query.filter_by(approved=True, soft_deleted=False)
|
||||
if package is not None:
|
||||
package_query = package_query.filter(Package.id != package.id)
|
||||
|
||||
enableWizard = name is None and request.method != "POST"
|
||||
return render_template("packages/create_edit.html", package=package, \
|
||||
form=form, author=author, enable_wizard=enableWizard)
|
||||
form=form, author=author, enable_wizard=enableWizard, \
|
||||
packages=package_query.all(), \
|
||||
mpackages=MetaPackage.query.order_by(db.asc(MetaPackage.name)).all())
|
||||
|
||||
@app.route("/packages/<author>/<name>/approve/", methods=["POST"])
|
||||
@login_required
|
||||
|
39
migrations/versions/4e482c47e519_.py
Normal file
39
migrations/versions/4e482c47e519_.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 4e482c47e519
|
||||
Revises: 900758871713
|
||||
Create Date: 2018-05-27 22:38:16.507155
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4e482c47e519'
|
||||
down_revision = '900758871713'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('dependency',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('depender_id', sa.Integer(), nullable=True),
|
||||
sa.Column('package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('meta_package_id', sa.Integer(), nullable=True),
|
||||
sa.Column('optional', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['depender_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['meta_package_id'], ['meta_package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('depender_id', 'package_id', 'meta_package_id', name='_dependency_uc')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('dependency')
|
||||
# ### end Alembic commands ###
|
57
migrations/versions/900758871713_.py
Normal file
57
migrations/versions/900758871713_.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 900758871713
|
||||
Revises: ea5a023711e0
|
||||
Create Date: 2018-05-27 16:36:44.258935
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '900758871713'
|
||||
down_revision = 'ea5a023711e0'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('meta_package',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('provides',
|
||||
sa.Column('package_id', sa.Integer(), nullable=False),
|
||||
sa.Column('metapackage_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['metapackage_id'], ['meta_package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('package_id', 'metapackage_id')
|
||||
)
|
||||
op.drop_table('harddeps')
|
||||
op.drop_table('softdeps')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('softdeps',
|
||||
sa.Column('package_id', sa.INTEGER(), nullable=False),
|
||||
sa.Column('dependency_id', sa.INTEGER(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['dependency_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('package_id', 'dependency_id')
|
||||
)
|
||||
op.create_table('harddeps',
|
||||
sa.Column('package_id', sa.INTEGER(), nullable=False),
|
||||
sa.Column('dependency_id', sa.INTEGER(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['dependency_id'], ['package.id'], ),
|
||||
sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
|
||||
sa.PrimaryKeyConstraint('package_id', 'dependency_id')
|
||||
)
|
||||
op.drop_table('provides')
|
||||
op.drop_table('meta_package')
|
||||
# ### end Alembic commands ###
|
19
setup.py
19
setup.py
@ -255,7 +255,6 @@ No warranty is provided, express or implied, for any part of the project.
|
||||
mod.title = "Sweet Foods"
|
||||
mod.license = licenses["CC0"]
|
||||
mod.type = PackageType.MOD
|
||||
mod.harddeps.append(food)
|
||||
mod.author = ruben
|
||||
mod.tags.append(tags["player_effects"])
|
||||
mod.repo = "https://github.com/rubenwardy/food_sweet/"
|
||||
@ -263,6 +262,7 @@ No warranty is provided, express or implied, for any part of the project.
|
||||
mod.forums = 9039
|
||||
mod.shortDesc = "Adds sweet food"
|
||||
mod.desc = "This is the long desc"
|
||||
food_sweet = mod
|
||||
db.session.add(mod)
|
||||
|
||||
game1 = Package()
|
||||
@ -314,6 +314,23 @@ Uses the CTF PvP Engine.
|
||||
rel.approved = True
|
||||
db.session.add(rel)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
metas = {}
|
||||
for package in Package.query.filter_by(type=PackageType.MOD).all():
|
||||
meta = None
|
||||
try:
|
||||
meta = metas[package.name]
|
||||
except KeyError:
|
||||
meta = MetaPackage(package.name)
|
||||
db.session.add(meta)
|
||||
metas[package.name] = meta
|
||||
package.provides.append(meta)
|
||||
|
||||
dep = Dependency(food_sweet, meta=metas["food"])
|
||||
db.session.add(dep)
|
||||
|
||||
|
||||
|
||||
delete_db = len(sys.argv) >= 2 and sys.argv[1].strip() == "-d"
|
||||
if delete_db and os.path.isfile("db.sqlite"):
|
||||
|
Loading…
Reference in New Issue
Block a user