]> git.lizzy.rs Git - cheatdb.git/commitdiff
Add meta package selector
authorrubenwardy <rw@rubenwardy.com>
Sun, 27 May 2018 19:15:35 +0000 (20:15 +0100)
committerrubenwardy <rw@rubenwardy.com>
Sun, 27 May 2018 19:22:01 +0000 (20:22 +0100)
README.md
app/models.py
app/public/static/tagselector.js
app/scss/components.scss
app/templates/macros/forms.html
app/templates/packages/create_edit.html
app/views/packages/__init__.py

index c5203e11f3c4588b34b23db6b06315245a2bac1d..8dc588b70301e86ce5a146781a540461795b3b86 100644 (file)
--- a/README.md
+++ b/README.md
@@ -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
index ea8bb18d9ca79a0aa26008e07b7c88a62e31bbe6..bfd8a31c2ba7c7757ff38302d28e92e927682b7a 100644 (file)
@@ -414,6 +414,19 @@ class MetaPackage(db.Model):
        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 = []
@@ -430,16 +443,7 @@ class MetaPackage(db.Model):
                        if not pattern.match(x):
                                continue
 
-                       mp = cache.get(x)
-                       if mp is None:
-                               mp = MetaPackage.query.filter_by(name=x).first()
-
-                       if mp is None:
-                               mp = MetaPackage(x)
-                               db.session.add(mp)
-
-                       cache[x] = mp
-                       retval.append(mp)
+                       retval.append(MetaPackage.GetOrCreate(x, cache))
 
                return retval
 
index d5895bfef7a5260727ac7e04efd826900cbbf02c..2e906574a8892727aa8319545e053ae30e51006f 100644 (file)
@@ -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);
                        });
        }
 
+       $.fn.csvSelector = function(source, name, result, allowSlash) {
+               return this.each(function() {
+                               var selector = $(this),
+                                       input = $('input[type=text]', this);
+
+                               var selected = [];
+
+                               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 = source[selected[i]] || selected[i];
+                                               addTag(selected[i], value);
+                                       }
+                                       result.val(selected.join(","))
+                               }
+                               recreate();
+
+                               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.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();
+                                                               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({
                        });
 
                        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);
                })
        });
 })(jQuery);
index 2cf8af4e7a6a0cf3849cb1a82189cd0b28dbcde1..a8ec31adbc0263eea2198a62caa524c0a658b68e 100644 (file)
@@ -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;
 }
index e0ad2dec884ebab69b3c0a7e665a3b76eb076dfc..430c4e8d6f5a8eb1c67b2f66dfc0d72e8ca326b1 100644 (file)
@@ -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>
@@ -45,7 +45,7 @@
                        {% if not label %}{% set label=field.label.text %}{% endif %}
                        <label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
                {% endif %}
-               <div class="metapackage_selector">
+               <div class="metapackage_selector bulletselector">
                        <input type="text" placeholder="Start typing to see suggestions">
                        <div class="clearboth"></div>
                </div>
index 8eb72138c0fe73dfc175783b3cd47feed8e8fc48..c7c2dbbfca5bb7a72900528197915c0dee495740 100644 (file)
 {% block content %}
        <h2>Create Package</h2>
 
+       <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 %}
+               ]
+       </script>
+
        {% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field, render_mpackage_field %}
        {{ form_includes() }}
 
index e5a515d213676e1b02aca9d562986f5720e70d01..3c3acf0ea2dc90f7a8ce2b769c59389111b53cf1 100644 (file)
@@ -106,7 +106,7 @@ class PackageForm(FlaskForm):
        desc          = TextAreaField("Long Description", [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", [InputRequired(), Length(1,1000)])
+       provides_str  = StringField("Provides", [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)
        repo          = StringField("Repo URL", [Optional(), URL()])
        website       = StringField("Website URL", [Optional(), URL()])
@@ -174,6 +174,11 @@ def create_edit_package_page(author=None, name=None):
                for m in mpackages:
                        package.provides.append(m)
 
+               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))
@@ -188,7 +193,8 @@ def create_edit_package_page(author=None, name=None):
 
        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, \
+                       mpackages=MetaPackage.query.order_by(db.asc(MetaPackage.name)).all())
 
 @app.route("/packages/<author>/<name>/approve/", methods=["POST"])
 @login_required