From 093ff2d28455899ef53cee74d0b5466ecf0a1eac Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 Jun 2018 18:20:41 +0200 Subject: [PATCH 01/31] updated README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index d03d3bd..96085b4 100755 --- a/README.md +++ b/README.md @@ -36,3 +36,13 @@ brew install imagemagick@6 Create a symlink to this newly installed dylib file as mentioned in other answer to get things working. ln -s /usr/local/Cellar/imagemagick@6//lib/libMagickWand-6.Q16.dylib /usr/local/lib/libMagickWand.dylib +## install pyrqlite + +git clone https://github.com/rqlite/pyrqlite.git +pip install ./pyrqlite + +## install sqlalchemy-rqlite + +git clone https://github.com/rqlite/sqlalchemy-rqlite.git +cd sqlalchemy-rqlite +sudo python3 ./setup.py install From 7938ec3591d4339da20da558825fe04138ce77b2 Mon Sep 17 00:00:00 2001 From: Michael Murtaugh Date: Wed, 6 Jun 2018 19:32:40 +0200 Subject: [PATCH 02/31] csv export --- app/views.py | 17 +++++++++++++++++ import_csv.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/views.py b/app/views.py index ccedbb1..acc2e22 100755 --- a/app/views.py +++ b/app/views.py @@ -394,6 +394,23 @@ def add_to_stack(id): db.session.commit() return render_template('show_stack_detail.html', stack=stack) +from csv import DictWriter +import io + +@app.route('/export/csv', methods=['GET']) +def export_csv (): + output = io.StringIO() + fieldnames = ['title', 'authors'] + csv = DictWriter(output,fieldnames) + csv.writeheader() + # for i in range(10): + # csv.writerow({'ID': i, 'fruit': "Tomato"}) + for book in Book.query.order_by("title"): + authors = ", ".join([x.author_name for x in book.authors]) + csv.writerow({"title": book.title, "authors": authors}) + resp = Response(output.getvalue(), mimetype="text/plain") + # resp.headers["Content-Disposition"] = "attachment;filename=export.csv" + return resp ### # The API ### diff --git a/import_csv.py b/import_csv.py index 9aa06d1..b43447d 100644 --- a/import_csv.py +++ b/import_csv.py @@ -39,7 +39,7 @@ with open(args.csv) as f: if stack: b = db.session.query(Stack).filter_by(stack_name=stack).first() if b == None: - b = Stack(stack_name=stack, stack_description=stack_description) + b = Stack(stack_name=stack, stack_description=None) db.session.add(b) book.stacks.append(b) From 68c38ae1983b012e53f6e97656c3d36b0b3653ec Mon Sep 17 00:00:00 2001 From: nberting Date: Thu, 7 Jun 2018 09:59:08 +0200 Subject: [PATCH 03/31] added upload new version of file into edit form --- app/forms.py | 1 + app/templates/edit_book_detail.html | 13 ++++++++++-- app/views.py | 29 ++++++++++++++++++++------ whoosh/Book/MAIN_taxstcerlfadcokk.seg | Bin 0 -> 5276 bytes whoosh/Book/_MAIN_4.toc | Bin 0 -> 2306 bytes 5 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 whoosh/Book/MAIN_taxstcerlfadcokk.seg create mode 100644 whoosh/Book/_MAIN_4.toc diff --git a/app/forms.py b/app/forms.py index 0db4fdf..e276a3d 100755 --- a/app/forms.py +++ b/app/forms.py @@ -25,6 +25,7 @@ class EditForm(FlaskForm): author = FieldList(FormField(AuthorForm, default=lambda: Author()), min_entries=1) category = StringField('category', validators=[InputRequired()]) year_published = StringField('year published', [validators.Length(max=4)],default=None) + file = FileField() class ChatForm(FlaskForm): message = StringField('message', validators=[InputRequired()]) diff --git a/app/templates/edit_book_detail.html b/app/templates/edit_book_detail.html index c936ff4..094aee6 100755 --- a/app/templates/edit_book_detail.html +++ b/app/templates/edit_book_detail.html @@ -7,7 +7,7 @@ -
+ {{ form.csrf_token }}

{{ form.title.label }} {{ form.title(size=20, class="form-control") }}

@@ -32,10 +32,19 @@
- {{ form.category.label }} {{ form.category(size=20, class="form-control") }} + {{ form.category.label }} {{ form.category(size=20, + class="form-control") }}

{{ form.year_published.label }} {{ form.year_published(size=4, class="form-control") }} +
+
+ Current file: {{ book.file }} +
+
+ + Upload new file: {{form.file}} +

diff --git a/app/views.py b/app/views.py index acc2e22..7d77122 100755 --- a/app/views.py +++ b/app/views.py @@ -134,16 +134,13 @@ def edit_book_by_id(id): if request.method == 'POST': if user_form.validate_on_submit(): - # check if the post request has the file part - title = user_form.title.data # You could also have used request.form['name'] - input_authors = user_form.author.data # You could also have used request.form['email'] + # on submit, check fields + title = user_form.title.data + input_authors = user_form.author.data category = user_form.category.data year_published = user_form.year_published.data if year_published=="": year_published = None - # save user to database - #book = Book(title, author, filename, cover, file_extension) - book = Book.query.filter_by(id=id).first() book.title = title book.category = category @@ -159,6 +156,26 @@ def edit_book_by_id(id): a = Author(author_name=author_name) db.session.add(a) book.authors.append(a) + + # editing / uploading new file + if user_form.file.data: + file = request.files['file'] + if file.filename == '': + flash('No selected file') + return redirect(request.url) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + allbooks = db.session.query(Book).all() + id = book.id + new_filename = str(id) +"_"+ filename + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) + name, file_extension = os.path.splitext(new_filename) + file.save(fullpath) + book.cover = get_cover(fullpath, name) + book.file = new_filename + else: + flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) + db.session.commit() flash("%s updated" % (title)) return redirect(url_for('show_book_by_id', id=id)) diff --git a/whoosh/Book/MAIN_taxstcerlfadcokk.seg b/whoosh/Book/MAIN_taxstcerlfadcokk.seg new file mode 100644 index 0000000000000000000000000000000000000000..a10c22262e80e2750ddc649e7aad5112616e3627 GIT binary patch literal 5276 zcmZQz00AL>1_q{HCLoIii04+sWhboCP3cHTN=P{2qnXl^z-Gkbdxm>v*H;Gj&#a;f zPRu}M3xRk|xUo~t|NsBzG%#s+GkP<)+kXIx0)aQPHwz)-!Nh4+6k&FCT!h0 zVeQ@tvHT3@0)*MAW|uPoy9|Zdpmtrv;x7ho22_82&el)4!ptDa!`zO?iOk;2sM=^4 z9HiM5OdvS&g*m9^uVMmr^$Bx&1bY}m5)p)C15(Ni44K8nr405BU@=A}CMXM{1VQEi znVdij)KFZInw*)KgI^CwJqWXc^)MQmn8VdGz*s;jIJg01nFBFIJyTL@3QQFvGc%@} z7=alJY(6`XR%T#I&PdEG!mpzmssrL}rjo=Ygcq<{@f@VVf6EFHAPoX`5DJq#=XFvF zD$0v2t2BRlAe4UzS=@f@!$v5-2w6P${rW3V{vu@YrsT*U5I!TQ7y$xCQwTkx*q~w* z2pD}KG}&yJIjKnIjIzOj!8jTY0}~FgG+B$3CP&$jxEKuw!r=f*KU0y?&nO!bZlmEa z8VH&ba3XJ%eXYDGz9LF$wqMvo~y9AJJmGWkdqpb7usPNOUD60>?IV z^z=Z(9PCEj+(DC#g^Rf)Z*$H3GwJc<&%r^HyLSeCcHmj(^o+x9fs{gm&6NWp(I+09 zov|YPgwR1Yk&~V}C(jt`q~vO+aBHQUKM-(|A>`yl#wpKuw4P*XKgrX7a*>&V>!y4M z)NwH&$1z8MdPbl=9UhmZB_w!w`Dy|k#=2>ev5;{$aHu4r;mxEYOXfH&`{tCk|H+Rh zf2Le{lCkoc4Wl`uEL=(U#%DS9Munw3R^911O@@eH}gh z;!6@Mic6AHi*nKuQ)kfZ~-S zH$Np482c$x+#RnSv-s4&z)(uq+_KbUn7J9u-n5u?WlP^lpl(+v6ScldXcu_U;ettWzO>drLzIaGCXEq{h?+pa zz7{;CKZYXtDhU}z5L%P8Nq4z_)?hzkOeJtYH#7tx-A+V=U_69_PDC`*!Kln98XPhv zQNOZ&QeUYp;NSEKI9xye5RNqAX`_Oww$%~v%yw))7TsgM%tK%0>m+h4K`yHc4US^$ zXP;=kBzdQA`5amvOW=83Q5C2W@PcESc8pLgG)>Km_y!3Dks7?%f|qn-2y?Oo99IJJ zd07dufLDy+Totn7l_YH!&J=bb%&03XBJi$~94myg zN(#Aza|Nz|StZy6<`gcq)(FfSgE>ujPnml>g7=jb3Lg}H9(-8%_28pI<-vjy)(L#9 z+$1G%o;-k0ns7l~1XzuW^`Szxx^StX+C3s+9N5^wm$&|=0RkR%sJ4U++JDb0_@198 zu&5iOn500B7s6-#R`9K`UF+}&yGF}(8q^i9{2s10ln-l%9LsWz;%he^z~@c4Zj2+A zW8s7f>~;9!+Vx*zn+RLkV*vsze6t^;I1BB1K2zB5LW3ntYB_-~bz=g7)JU5)4!?x2 zt}Oh4SNK!o&O6EjZX{sDwz77(9|xf+dmREdi<*cI@qLCF-`Y0zqig?c;)r>y3%3jI zxs?ek#uSWRuf`^+oPr8xx}+j19w!dg~KDu_6e9dr7Y)u>L4o_lcy}VmnsFG(~Ke zyS|sehE7LX@HHK=sNI5ZsBKZ&f`nq3L<_#9!irUMm~JoZex!(HOkio;Bnj^CJBBJF^LGW#i;zMmy%I?h6ubVDm3Sz5VvezB3I zBZ9P^u5yHFFR?Q>_l{_XOmTgXHIZty|256(L+OZmR?I5K=Nc}GK}X9H z&?=Ijjn2;55kE(Qo;@=^mr;#QmUjE=n{=u^Ob^tN13g$Dq=&xG^!mUL^zarvLXXm8 r|0SZdx53sdaG literal 0 HcmV?d00001 From 1a68728ed1a310b899529bcdcd66c547698d27fe Mon Sep 17 00:00:00 2001 From: nberting Date: Thu, 7 Jun 2018 10:46:14 +0200 Subject: [PATCH 04/31] expanded search functionality --- app/__init__.py | 9 ++++----- app/forms.py | 5 ++++- app/models.py | 1 - app/static/css/style.css | 19 ++++--------------- app/templates/results.html | 25 ++++++++++++++++--------- app/templates/show_books.html | 4 ++-- app/views.py | 27 ++++++++++++++++++++------- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index b2dc543..fecc4ff 100755 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,7 +8,7 @@ import os import click from werkzeug.utils import secure_filename from sqlalchemy.dialects import registry -import flask_whooshalchemyplus +# import flask_whooshalchemyplus not using whoosh anymore registry.register("rqlite.pyrqlite", "sqlalchemy_rqlite.pyrqlite", "dialect") @@ -27,9 +27,6 @@ app.config['SQLALCHEMY_DATABASE_URI'] = 'rqlite+pyrqlite://localhost:4001/' app.config['DEBUG'] = True app.config['PORT'] = 80 -# set the location for the whoosh index -app.config['WHOOSH_BASE'] = 'whoosh' - #app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'mydatabase.db') db = SQLAlchemy(app) @@ -39,5 +36,7 @@ socketio = SocketIO(app) app.config.from_object(__name__) from app import views -flask_whooshalchemyplus.init_app(app) # initialize +# set the location for the whoosh index +# app.config['WHOOSH_BASE'] = 'whoosh' +# flask_whooshalchemyplus.init_app(app) # initialize diff --git a/app/forms.py b/app/forms.py index e276a3d..0e5e25f 100755 --- a/app/forms.py +++ b/app/forms.py @@ -46,6 +46,9 @@ class EditStackForm(FlaskForm): class SearchForm(FlaskForm): choices = [('All', 'All'), ('Title', 'Title'), - ('Category', 'Category')] + ('Author', 'Author'), + ('Category', 'Category'), + ('Stack', 'Stack')] select = SelectField('', choices=choices) search = StringField('', validators=[InputRequired()]) + diff --git a/app/models.py b/app/models.py index d2361e5..80868ec 100755 --- a/app/models.py +++ b/app/models.py @@ -17,7 +17,6 @@ stacks = db.Table('books_stacks', class Book(db.Model): __tablename__ = 'books' - __searchable__ = ['title', 'category', 'fileformat'] # these fields will be indexed by whoosh id = db.Column(db.Integer, primary_key = True) title = db.Column(db.String(255)) diff --git a/app/static/css/style.css b/app/static/css/style.css index be7e00f..a39c5b2 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -1,15 +1,4 @@ @import url("../fonts/fonts_style.css"); -/* -Font-names: -junicoderegular -junicoderegularcondensed -junicodeitalic -junicodeitaliccondensed -junicodebold -junicodeboldcondensed -junicodebolditalic -junicodebolditaliccondensed -*/ *{ font-family: "Archivo Narrow"; @@ -121,7 +110,7 @@ cursor: pointer; .header input{ height:40px; width: 500px; -font-size: 30px; +font-size: 24px; font-weight: bold; } @@ -136,10 +125,10 @@ margin: 0; float: left; width: 320px; height: 36px; -font-size: 20px; +font-size: 18px; font-weight: regular; padding: 2px; -background:rgba(50, 50, 50, 0.2); +background:rgb(240, 240, 240); border:0px; box-shadow: inset 0 0 5px rgba(000,000,000, 0.2); } @@ -200,7 +189,7 @@ font-size: 12px; top:0; left:0; position: fixed; - font-size: 20px; + font-size: 18px; background-color: yellow; } diff --git a/app/templates/results.html b/app/templates/results.html index aa1acf7..1ee29ef 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -3,16 +3,21 @@ {% block main %}
+
{% from "_formhelpers.html" import render_field %}
{{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
- + - -
-

Search Results for: {{ query }}

+
+
+

Results: "{{ query }}" included in {{ count }} out of {{ whole }} items

+
+
+
+
{% with messages = get_flashed_messages() %} {% if messages %}
@@ -53,11 +58,11 @@
  • {{ stack.stack_name }}
  • {% endfor %} - - + ==> - {% endfor %} + {% endfor %} +
    @@ -92,11 +97,13 @@ {% for stack in book.stacks %}
  • {{ stack.stack_name }}
  • - {% endfor %} + {% endfor %} + - + ==> + {% endfor %}

    diff --git a/app/templates/show_books.html b/app/templates/show_books.html index 101ce54..5f8e7b9 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -7,7 +7,7 @@

    {{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
    - +

    All Books

    @@ -60,7 +60,7 @@
  • {{ stack.stack_name }}
  • {% endfor %} - + ==> diff --git a/app/views.py b/app/views.py index 7d77122..58edf45 100755 --- a/app/views.py +++ b/app/views.py @@ -335,7 +335,6 @@ def edit_stack_by_id(id): return redirect(url_for('show_stack_by_id', id=id)) return render_template('edit_stack_detail.html', stack=stack, form=form) -## search ## search @@ -352,18 +351,28 @@ def show_books(): def search_results(searchtype, query): search = SearchForm(request.form) random_order=Book.query.order_by(func.random()).limit(10) - results=Book.query.filter(Book.title.contains(query)).all() + results=Book.query.filter(Book.title.contains(query)) if searchtype == 'Title': - results=Book.query.filter(Book.title.contains(query)).all() + results=Book.query.filter(Book.title.contains(query)) if searchtype == 'Category': - results=Book.query.filter(Book.category.contains(query)).all() + results=Book.query.filter(Book.category.contains(query)) + + if searchtype== 'Author': + results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)) + + if searchtype== 'Stack': + results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)) if searchtype== 'All': - results=Book.query.whoosh_search(query).all() + # results=Book.query.whoosh_search(query) + results=Book.query.filter(Book.title.contains(query)) + results=results.union(Book.query.filter(Book.category.contains(query))) + results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query))) + results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))) - if not results: + if results.count() == 0: upload_form = UploadForm(title= query, author='') return render_template('red_link.html', form=upload_form, title=query) @@ -372,7 +381,11 @@ def search_results(searchtype, query): results = [] return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) - return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query) + count = results.count() + whole = Book.query.count() + percentage = float(count / whole * 100) + return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) + ## Search - autocomplete autocomplete_suggestions = [] From c6b99974dad14b90caa19eb93056b5350ed02db9 Mon Sep 17 00:00:00 2001 From: nberting Date: Thu, 7 Jun 2018 19:10:56 +0200 Subject: [PATCH 05/31] new default covers and grid view in search --- app/forms.py | 3 + app/static/css/style.css | 36 +++++++++++ app/static/img/default_cover.gif | Bin 0 -> 14473 bytes app/templates/add_to_stacks.html | 2 +- app/templates/edit_book_detail.html | 2 +- app/templates/results.html | 34 ++++++----- app/templates/results_grid.html | 89 ++++++++++++++++++++++++++++ app/templates/show_book_detail.html | 2 +- app/templates/show_books.html | 20 ++++--- app/templates/show_books_grid.html | 41 ++++++++----- app/views.py | 24 ++++++-- 11 files changed, 204 insertions(+), 49 deletions(-) create mode 100644 app/static/img/default_cover.gif create mode 100644 app/templates/results_grid.html diff --git a/app/forms.py b/app/forms.py index 0e5e25f..81508e1 100755 --- a/app/forms.py +++ b/app/forms.py @@ -51,4 +51,7 @@ class SearchForm(FlaskForm): ('Stack', 'Stack')] select = SelectField('', choices=choices) search = StringField('', validators=[InputRequired()]) + grid = SubmitField('Grid') + listview = SubmitField('List') + diff --git a/app/static/css/style.css b/app/static/css/style.css index a39c5b2..b72808f 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -313,3 +313,39 @@ box-sizing: border-box; height: 100%; border: none; } + +.grid { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + grid-gap: 2px; + align-items: top; + justify-items: center; +} + +@media screen and (max-width: 900px) { +.grid{ + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-gap: 2px; + align-items: center; + justify-items: center; +} +} +@media screen and (max-width: 400px) { +.grid{ + display: grid; + grid-template-columns: 1fr; + align-items: center; + justify-items: center; +} +} + +.gridbox { + display: inline-block; + padding-left: 5px; + align-items: center; + justify-items: center; +} +.gridbox:hover{ + opacity: 0.5; +} \ No newline at end of file diff --git a/app/static/img/default_cover.gif b/app/static/img/default_cover.gif new file mode 100644 index 0000000000000000000000000000000000000000..500390cbbb1928bb76ffe47ff34e0784f54b5609 GIT binary patch literal 14473 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 bZ$9U5KId;f=WjmeZ$9U5KIeZopCkNVFY5BI literal 0 HcmV?d00001 diff --git a/app/templates/add_to_stacks.html b/app/templates/add_to_stacks.html index c2ed248..2e7ab09 100644 --- a/app/templates/add_to_stacks.html +++ b/app/templates/add_to_stacks.html @@ -6,7 +6,7 @@
    Chosen book:

    {{ book.title }}

    - +

    These are all the stacks that have been built so far.

    diff --git a/app/templates/edit_book_detail.html b/app/templates/edit_book_detail.html index 094aee6..4299e95 100755 --- a/app/templates/edit_book_detail.html +++ b/app/templates/edit_book_detail.html @@ -12,7 +12,7 @@

    {{ form.title.label }} {{ form.title(size=20, class="form-control") }}

    - +

    diff --git a/app/templates/results.html b/app/templates/results.html index 1ee29ef..54f4546 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -1,19 +1,20 @@ {% extends 'base.html' %} {% block main %} -
    -
    {% from "_formhelpers.html" import render_field %}
    {{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
    -
    +


    + {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    +
    +
    -

    Results: "{{ query }}" included in {{ count }} out of {{ whole }} items

    +

    Results: "{{ query }}" included in {{ count }} out of {{ whole }} items

    @@ -34,16 +35,17 @@ Cover Title - Author - Filetype - Category - Stack - Add to stack + Author + Filetype + Category + Stack + Add to stack {% for book in books %} - + + {{ book.title }} {% for author in book.authors %} @@ -75,16 +77,16 @@ Cover Title - Author - Filetype - Category - Stack - Add to stack + Author + Filetype + Category + Stack + Add to stack {% for book in books_all %} - + {{ book.title }} {% for author in book.authors %} diff --git a/app/templates/results_grid.html b/app/templates/results_grid.html new file mode 100644 index 0000000..cea341b --- /dev/null +++ b/app/templates/results_grid.html @@ -0,0 +1,89 @@ +{% extends 'base.html' %} + +{% block main %} +
    + {% from "_formhelpers.html" import render_field %} +
    +
    {{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
    + + +


    + {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    +
    +
    + +
    +

    Results: "{{ query }}" included in {{ count }} out of {{ whole }} items

    +
    +
    +
    +
    + {% with messages = get_flashed_messages() %} + {% if messages %} +
    +
      + {% for message in messages %} +
    • {{ message }}
    • + {% endfor %} +
    +
    + {% endif %} + {% endwith %} +
    + + {% for book in books|sort(attribute='title', reverse = False) %} + + +
    + + +

    + + +{{ book.title }} +
    + +{% for author in book.authors %} +{{ author.author_name }}
    {% endfor %}
    + +

    +
    + {% endfor %} +
    + + +
    +
    +

    Other books

    + +
    + +{% for book in books_all|sort(attribute='title', reverse = False) %} + +
    + + +

    + + +{{ book.title }} +
    + +{% for author in book.authors %} +{{ author.author_name }}
    {% endfor %}
    + +

    +
    + {% endfor %} +
    +
    + + +

    + See all books +

    + +
    + +{% endblock %} diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index 42cd67d..a6ab30f 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -4,7 +4,7 @@

    {{ book.title }}

    - +

    Author(s):

      {% for author in book.authors %}
    • {{ author.author_name }}
    • diff --git a/app/templates/show_books.html b/app/templates/show_books.html index 5f8e7b9..258c0e6 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -8,7 +8,9 @@ - +


      + {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

      +

      All Books

      {% with messages = get_flashed_messages() %} @@ -28,19 +30,19 @@ Cover Title - Author - Filetype - Category - Stack - Add to stack + Author + Filetype + Category + Stack + Add to stack {% for book in books|sort(attribute='title', reverse = False) %} - + - + +
      + + +

      + + +{{ book.title }} +
      + +{% for author in book.authors %} +{{ author.author_name }}
      {% endfor %}
      + +

      +
      {% endfor %} - +
    {% endblock %} diff --git a/app/views.py b/app/views.py index 58edf45..e152071 100755 --- a/app/views.py +++ b/app/views.py @@ -342,6 +342,11 @@ def edit_stack_by_id(id): def show_books(): books = db.session.query(Book).all() search = SearchForm(request.form) + + if search.grid.data: + return render_template ('show_books_grid.html', books=books, form=search) + if search.listview.data: + return render_template ('show_books.html', books=books, form=search) if request.method == 'POST': return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) @@ -349,8 +354,8 @@ def show_books(): @app.route('/search///', methods=['POST', 'GET']) def search_results(searchtype, query): - search = SearchForm(request.form) - random_order=Book.query.order_by(func.random()).limit(10) + search = SearchForm(request.form, search=query) + random_order=Book.query.order_by(func.random()).limit(14) results=Book.query.filter(Book.title.contains(query)) if searchtype == 'Title': @@ -376,14 +381,21 @@ def search_results(searchtype, query): upload_form = UploadForm(title= query, author='') return render_template('red_link.html', form=upload_form, title=query) + count = results.count() + whole = Book.query.count() + percentage = float(count / whole * 100) + + if search.listview.data: + return render_template('results.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) + + if search.grid.data: + return render_template('results_grid.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) + if request.method == 'POST': query = search.search.data results = [] return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) - - count = results.count() - whole = Book.query.count() - percentage = float(count / whole * 100) + return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) From 869ed3f3bbf14a6b8768561d33201156b95aff53 Mon Sep 17 00:00:00 2001 From: Alice Date: Thu, 7 Jun 2018 19:27:09 +0200 Subject: [PATCH 06/31] edited pdf view --- app/static/css/style.css | 8 +- app/templates/show_stack_detail.html | 2 +- app/views_2.py | 539 +++++++++++++++++++++++++++ 3 files changed, 545 insertions(+), 4 deletions(-) create mode 100755 app/views_2.py diff --git a/app/static/css/style.css b/app/static/css/style.css index b72808f..de6ccad 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -301,9 +301,11 @@ box-sizing: border-box; .widget { resize: both; overflow: hidden; - width: 300px; - height: 300px; + width: 800px; + height: 200px; display: inline-block; + position: relative; + left: 400px; } @@ -348,4 +350,4 @@ box-sizing: border-box; } .gridbox:hover{ opacity: 0.5; -} \ No newline at end of file +} diff --git a/app/templates/show_stack_detail.html b/app/templates/show_stack_detail.html index af144cd..fda0469 100644 --- a/app/templates/show_stack_detail.html +++ b/app/templates/show_stack_detail.html @@ -9,7 +9,7 @@

    Books in this stack: {% for book in stack.books %}

  • {{book.title}}
  • - +
    diff --git a/app/views_2.py b/app/views_2.py new file mode 100755 index 0000000..edf46ac --- /dev/null +++ b/app/views_2.py @@ -0,0 +1,539 @@ +""" +Flask Documentation: http://flask.pocoo.org/docs/ +Jinja2 Documentation: http://jinja.pocoo.org/2/documentation/ +Werkzeug Documentation: http://werkzeug.pocoo.org/documentation/ +This file creates your application. +""" + +from app import app, db, socketio, DOMAIN +from flask import Flask, Response, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort +import json +from sqlalchemy.sql.expression import func, select +from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm +from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema +from app.cover import get_cover +from os import environ +from flask_socketio import SocketIO, emit +import datetime +import time +import autocomplete +import sys +from csv import DictWriter +import io + +import os +from werkzeug.utils import secure_filename + +# import sqlite3 +ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'chm', 'mobi']) + +author_schema = AuthorSchema() +authors_schema = AuthorSchema(many=True) +book_schema = BookSchema() +books_schema = BookSchema(many=True) +stack_schema = StackSchema() +stacks_schema = StackSchema(many=True) +chat_schema = ChatSchema() +chats_schema = ChatSchema(many=True) + +def allowed_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS +### +# Routing for your application. +### + +@app.route('/', methods= ['POST','GET']) +def home(): + chat_form = ChatForm() + chat_messages = db.session.query(Chat).all() + + # if request.method == 'POST': + # if chat_form.validate_on_submit(): + # message = chat_form.message.data + # msg = Chat(message) + # db.session.add(msg) + # db.session.commit() + + return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username="librarian") + +@app.route('/hello/') +def hello(name): + return "Hello " + name + +@app.route('/about/') +def about(): + """Render the website's about page.""" + return render_template('about.html', name="Mary Jane") + +@app.route('/uploads/') +def uploaded_file(filename): + return send_from_directory(app.config['UPLOAD_FOLDER'], + filename) + +@app.route('/uploads/cover/') +def uploaded_file_cover(filename): + return send_from_directory(app.config['UPLOAD_FOLDER_COVER'], + filename) + +@app.route('/updates', methods=['POST', 'GET']) +def get_updates(): + userin = UserIns.query.filter_by(title="lastViewed").first() + allbooks = db.session.query(Book).all() + id = len(allbooks) + latest_upload = allbooks[-1] + return "This is the XPPL ~ Library XPUB ~ Updates / / / / / / / Last viewed: " + userin.info + " / / / / / / / " + str(len(allbooks)) + " Books online "+ " / / / / / / / " + "Latest upload: " + latest_upload.title + +@app.route('/scape', methods=['POST', 'GET']) +def scape(): + if request.method == 'POST': + data = request.form + book = Book.query.get(data['id']) + print(book.scapeX) + book.scapeX = data['x'] + book.scapeY = data['y'] + db.session.commit() + books = db.session.query(Book).all() # or you could have used User.query.all() + return render_template('scape.html', books=books) + + +@app.route('/books_grid') +def show_books_grid(): + books = db.session.query(Book).all() # or you could have used User.query.all() + return render_template('show_books_grid.html', books=books) + +@app.route('/books/') +def show_book_by_id(id): + book = Book.query.get(id) + userin = UserIns.query.filter_by(title="lastViewed").first() + if userin != None: + userin.info = book.title + db.session.commit() + else: + user_info = UserIns("lastViewed", book.title) + db.session.add(user_info) + db.session.commit() + if not book: + return render_template('red_link.html', id=id) + else: + return render_template('show_book_detail.html', book=book) + + +@app.route('/books//delete', methods=['POST', 'GET']) +def remove_book_by_id(id): + book_to_edit = Book.query.filter_by(id=id).first() + title = book_to_edit.title + Book.query.filter_by(id=id).delete() + #author_table = Author.query.filter_by(book_id=book_to_edit.id).delete() + db.session.commit() + flash("%s deleted from library" % (title)) + return redirect(url_for('show_books')) + +@app.route('/books//edit', methods=['POST', 'GET']) +def edit_book_by_id(id): + book_to_edit = Book.query.filter_by(id=id).first() + user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published) + + if request.method == 'POST': + if user_form.validate_on_submit(): + # on submit, check fields + title = user_form.title.data + input_authors = user_form.author.data + category = user_form.category.data + year_published = user_form.year_published.data + if year_published=="": + year_published = None + book = Book.query.filter_by(id=id).first() + book.title = title + book.category = category + book.year_published = year_published + + #authors update + book.authors.clear() + for i, author in enumerate(input_authors): + author_name = author.get("author_name") + if author_name: + a = db.session.query(Author).filter_by(author_name=author_name).first() + if a == None: + a = Author(author_name=author_name) + db.session.add(a) + book.authors.append(a) + + # editing / uploading new file + if user_form.file.data: + file = request.files['file'] + if file.filename == '': + flash('No selected file') + return redirect(request.url) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + allbooks = db.session.query(Book).all() + id = book.id + new_filename = str(id) +"_"+ filename + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) + name, file_extension = os.path.splitext(new_filename) + file.save(fullpath) + book.cover = get_cover(fullpath, name) + book.file = new_filename + else: + flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) + + db.session.commit() + flash("%s updated" % (title)) + return redirect(url_for('show_book_by_id', id=id)) + + return render_template('edit_book_detail.html', book=book_to_edit, form=user_form) + + +@app.route('/add-book', methods=['POST', 'GET']) +def add_book(): + upload_form = UploadForm() + + if request.method == 'POST': + if upload_form.validate_on_submit(): + #get data from form + title = upload_form.title.data + authors = upload_form.author.data + category = upload_form.category.data + year_published = upload_form.year_published.data + if year_published=="": + year_published = None + + #if upload with file + if upload_form.upload.data: + # check if the post request has the file part + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) + file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return redirect(request.url) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + allbooks = db.session.query(Book).all() + id = len(allbooks)+1 + new_filename = str(id) +"_"+ filename + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) + name, file_extension = os.path.splitext(new_filename) + file.save(fullpath) + cover = get_cover(fullpath, name) + else: + flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) + #if upload without file -> wishform, with potential PDF + if upload_form.wish.data: + #TO DO: make pdf generator + #file = open('app/uploads/potential.pdf') + #filename = 'potential.pdf' + #file_extension = '.pdf' + filename = '' + file_extension = '' + cover = '' + + book = Book(title, filename, cover, file_extension, category,year_published) + db.session.add(book) + for author in authors: + author_name = author.get("author_name") + if author_name: + a = db.session.query(Author).filter_by(author_name=author_name).first() + if a == None: + a = Author(author_name=author_name) + db.session.add(a) + book.authors.append(a) + db.session.commit() + + flash("%s added to the library" % (title)) + return redirect(url_for('show_books')) + + flash_errors(upload_form) + return render_template('add_book.html', form=upload_form) + + +# Flash errors from the form if validation fails +def flash_errors(form): + for field, errors in form.errors.items(): + for error in errors: + flash(u"Error in the %s field - %s" % ( + getattr(form, field).label.text, + error + )) + +#Authors + +@app.route('/authors/') +def show_author_by_id(id): + author = Author.query.get(id) + if not author: + abort (404) + else: + return render_template('show_author_detail.html', author=author) + + +@app.route('/authors//edit', methods=['POST', 'GET']) +def edit_author_by_id(id): + return "Ask the programmer." + +##stacks + +@app.route('/stacks') +def show_stacks(): + stacks = db.session.query(Stack).all() + return render_template('show_stacks.html', stacks=stacks) + +@app.route('/stacks/add_stack', methods=['POST', 'GET']) +def add_stack(): + form = StackForm() + stacks = db.session.query(Stack).all() + + if form.validate_on_submit(): + stack_name = form.stack_name.data + stack_description = form.stack_description.data + stack = Stack(stack_name, stack_description) + if form.stack_name.data: + stack = Stack(stack_name, stack_description) + db.session.add(stack) + stacks = db.session.query(Stack).all() + return redirect(url_for('show_stacks')) + flash("%s stack created" % (stack_name)) + return render_template('add_stack.html', stacks=stacks, form=form) + +@app.route('/stacks/tab/', methods=['POST', 'GET']) +def show_stack_in_tab(id): + return show_stack_by_id(id, is_tab=True) + + +@app.route('/stacks/', methods=['POST', 'GET']) +def show_stack_by_id(id, is_tab=False): + + stack = Stack.query.get(id) + if not stack: + abort (404) + else: + if is_tab == False: + return render_template('show_stack_detail.html', stack=stack) + else: + return render_template('show_stack_detail_tab.html', stack=stack) + +@app.route('/stacks//delete', methods=['POST', 'GET']) +def remove_stack_by_id(id): + Stack.query.filter_by(id=id).delete() + db.session.commit() + return redirect(url_for('show_stacks')) + +@app.route('/stacks//edit', methods=['POST', 'GET']) +def edit_stack_by_id(id): + stack = Stack.query.filter_by(id=id).first() + form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description) + + if request.method == 'POST': + if form.validate_on_submit(): + stack_name = form.edit_stack_name.data + stack_description = form.edit_stack_description.data + stack.stack_name = stack_name + stack.stack_description = stack_description + db.session.commit() + + return redirect(url_for('show_stack_by_id', id=id)) + return render_template('edit_stack_detail.html', stack=stack, form=form) + +#@app.route('/stacks//remove/', methods=['POST', 'GET']) +#def remove_from_stack(bookid, stackid): +# import ipdb; ipdb.set_trace() +# book = Book.query.get(id) +# stack = Stack.query.get(id) + +# stack = db.session.query(Stack).join(stack.books).filter_by(stackid=stackid) +# stack.books.delete(book) +# db.session.commit() +# return render_template('show_book_by_id.html', stackid=stack.id, bookid=book.id) + +@app.route('/add_to_stack/', methods=['GET', 'POST']) +def add_to_stack(id): + stacks = db.session.query(Stack).all() + add_form = AddtoStackForm(request.form) + add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks] + if request.method == 'GET': + book = Book.query.get(id) + return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form) + else: + stack = Stack.query.get(int(add_form.select_stack.data)) + book = Book.query.get(id) + stack.books.append(book) + db.session.commit() + return render_template('show_stack_detail.html', stack=stack) + +## search + +@app.route('/books', methods= ['POST','GET']) +def show_books(): + books = db.session.query(Book).all() + search = SearchForm(request.form) + if request.method == 'POST': + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) + + return render_template('show_books.html', books=books, form=search) + +@app.route('/search///', methods=['POST', 'GET']) +def search_results(searchtype, query): + search = SearchForm(request.form) + random_order=Book.query.order_by(func.random()).limit(10) + results=Book.query.filter(Book.title.contains(query)) + + if searchtype == 'Title': + results=Book.query.filter(Book.title.contains(query)) + + if searchtype == 'Category': + results=Book.query.filter(Book.category.contains(query)) + + if searchtype== 'Author': + results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)) + + if searchtype== 'Stack': + results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)) + + if searchtype== 'All': + # results=Book.query.whoosh_search(query) + results=Book.query.filter(Book.title.contains(query)) + results=results.union(Book.query.filter(Book.category.contains(query))) + results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query))) + results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))) + + if results.count() == 0: + upload_form = UploadForm(title= query, author='') + return render_template('red_link.html', form=upload_form, title=query) + + if request.method == 'POST': + query = search.search.data + results = [] + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) + + count = results.count() + whole = Book.query.count() + percentage = float(count / whole * 100) + return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) + + +## Search - autocomplete +autocomplete_suggestions = [] + +@app.route('/autocomplete_suggestions', methods=['GET', 'POST']) +def test1(): + if request.method == 'POST': + autocomplete.load() + query = request.form['search'] + query_tokenized = query.lower().split() + print(query_tokenized) + word_1 = query_tokenized[-2] + word_2 = query_tokenized[-1] + #print(word_1) + autocomplete_output = autocomplete.predict(word_1 , word_2) + autocomplete_suggestions.clear() + for suggestion, score in autocomplete_output: + autocomplete_suggestions.append(suggestion) + + print(autocomplete_suggestions) + + return Response(json.dumps(autocomplete_suggestions), mimetype='application/json') + + + + + +@app.route('/export/csv', methods=['GET']) +def export_csv (): + output = io.StringIO() + fieldnames = ['title', 'authors'] + csv = DictWriter(output,fieldnames) + csv.writeheader() + # for i in range(10): + # csv.writerow({'ID': i, 'fruit': "Tomato"}) + for book in Book.query.order_by("title"): + authors = ", ".join([x.author_name for x in book.authors]) + csv.writerow({"title": book.title, "authors": authors}) + resp = Response(output.getvalue(), mimetype="text/plain") + # resp.headers["Content-Disposition"] = "attachment;filename=export.csv" + return resp +### +# The API +### + +@app.route('/api/books', methods=['GET']) +def get_books(): + books = Book.query.all() + data = books_schema.dump(books) + #print(errors) + return jsonify({'books': data}) + +@app.route('/api/books/', methods=['GET']) +def get_book_by_id(id): + book = Book.query.get(id) + data = book_schema.dump(book) + if not data: + return jsonify({"message": "Book could not be found."}), 400 + else: + return jsonify({'book': data }) + +@app.route('/api/chats', methods=['GET']) +def get_chat(): + chats = Chat.query.all() + data = chats_schema.dump(chats) + #print(errors) + return jsonify({'chat': data}) + + + +### +# The functions below should be applicable to all Flask apps. +### + +@app.route('/.txt') +def send_text_file(file_name): + """Send your static text file.""" + file_dot_text = file_name + '.txt' + return app.send_static_file(file_dot_text) + + +@app.after_request +def add_header(response): + """ + Add headers to both force latest IE rendering engine or Chrome Frame, + and also to cache the rendered page for 10 minutes. + """ + response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1' + response.headers['Cache-Control'] = 'public, max-age=600' + return response + + +@app.errorhandler(404) +def page_not_found(error): + """Custom 404 page.""" + return render_template('404.html'), 404 + +@socketio.on('new_message') +def new_message(message): + # Send message to alls users + print("new message") + emit('channel-' + str(message['channel']), { + 'username': message['username'], + 'text': message['text'], + 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M")) + }, + broadcast=True + ) + # Save message + my_new_chat = Chat( + message=message['text'] + ) + db.session.add(my_new_chat) + try: + db.session.commit() + except: + db.session.rollback() + + + +if __name__ == '__main__': + socketio.run(app) + #app.run(debug=True,host="0.0.0.0",port="8080") From 2d8110585d626132a62b77611223a20bd4f19b56 Mon Sep 17 00:00:00 2001 From: Alice Date: Thu, 7 Jun 2018 23:05:55 +0200 Subject: [PATCH 07/31] remove book from stacks --- app/templates/show_book_detail.html | 5 ++++- app/templates/show_stack_detail.html | 2 +- app/views.py | 17 ++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index a6ab30f..da7e121 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -4,7 +4,8 @@

    {{ book.title }}

    - + +

    Author(s):

      {% for author in book.authors %}
    • {{ author.author_name }}
    • @@ -16,6 +17,8 @@

      Stack(s):

      diff --git a/app/templates/show_stack_detail.html b/app/templates/show_stack_detail.html index fda0469..e7c5193 100644 --- a/app/templates/show_stack_detail.html +++ b/app/templates/show_stack_detail.html @@ -9,7 +9,7 @@

      Books in this stack: {% for book in stack.books %}

    • {{book.title}}
    • - +
      diff --git a/app/views.py b/app/views.py index e152071..2fe1bc3 100755 --- a/app/views.py +++ b/app/views.py @@ -136,7 +136,7 @@ def edit_book_by_id(id): if user_form.validate_on_submit(): # on submit, check fields title = user_form.title.data - input_authors = user_form.author.data + input_authors = user_form.author.data category = user_form.category.data year_published = user_form.year_published.data if year_published=="": @@ -336,6 +336,17 @@ def edit_stack_by_id(id): return redirect(url_for('show_stack_by_id', id=id)) return render_template('edit_stack_detail.html', stack=stack, form=form) +@app.route('/stacks//remove/', methods=['POST', 'GET']) +def remove_from_stack(bookid, stackid): + book = Book.query.get(bookid) + stack = Stack.query.get(stackid) + if book not in stack.books: + return render_template('show_book_detail.html', book=book) + stack.books.remove(book) + db.session.commit() + return render_template('show_book_detail.html', book=book) + + ## search @app.route('/books', methods= ['POST','GET']) @@ -383,7 +394,7 @@ def search_results(searchtype, query): count = results.count() whole = Book.query.count() - percentage = float(count / whole * 100) + percentage = float(count / whole * 100) if search.listview.data: return render_template('results.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) @@ -395,7 +406,7 @@ def search_results(searchtype, query): query = search.search.data results = [] return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) - + return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) From 3296a2efcf20467d00b6749b2aefb354d6d4832c Mon Sep 17 00:00:00 2001 From: nberting Date: Thu, 7 Jun 2018 23:33:22 +0200 Subject: [PATCH 08/31] updates to grid and list view --- app/forms.py | 7 +++++-- app/templates/results.html | 8 ++++---- app/templates/results_grid.html | 2 +- app/templates/show_books.html | 8 ++++---- app/templates/show_books_grid.html | 18 ++++++++++++++++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/forms.py b/app/forms.py index 81508e1..f1704ab 100755 --- a/app/forms.py +++ b/app/forms.py @@ -3,7 +3,7 @@ from wtforms import StringField, FileField, validators from wtforms.validators import InputRequired, DataRequired from wtforms import FieldList from wtforms import Form as NoCsrfForm -from wtforms.fields import StringField, FormField, SubmitField, SelectField +from wtforms.fields import StringField, FormField, SubmitField, SelectField, RadioField from app.models import Book, BookSchema, Author, Stack, StackSchema # - - - Forms - - - @@ -49,9 +49,12 @@ class SearchForm(FlaskForm): ('Author', 'Author'), ('Category', 'Category'), ('Stack', 'Stack')] - select = SelectField('', choices=choices) + select = SelectField('', choices=choices, default='All') search = StringField('', validators=[InputRequired()]) grid = SubmitField('Grid') listview = SubmitField('List') + randomize = SubmitField('Order differently') + + diff --git a/app/templates/results.html b/app/templates/results.html index 54f4546..6ccb272 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -9,7 +9,7 @@ {{ render_field(form.search) }}


    - {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    + {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    @@ -33,7 +33,7 @@ - + @@ -45,7 +45,7 @@ {% for book in books %} + - + @@ -53,7 +53,7 @@
  • {{ author.author_name }}
  • {% endfor %} - + - + @@ -94,7 +94,7 @@
  • {{ author.author_name }}
  • {% endfor %} - + - + @@ -56,7 +56,7 @@
  • {{ author.author_name }}
  • {% endfor %} - +
    - {{ book.title }} {% for author in book.authors %} @@ -75,7 +75,7 @@ - + diff --git a/app/templates/results_grid.html b/app/templates/results_grid.html index cea341b..5b2a1ec 100644 --- a/app/templates/results_grid.html +++ b/app/templates/results_grid.html @@ -36,7 +36,7 @@
    - +

    diff --git a/app/templates/show_books.html b/app/templates/show_books.html index 258c0e6..69be928 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -9,7 +9,7 @@ {{ render_field(form.search) }}


    - {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    + {{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}

    All Books

    @@ -28,7 +28,7 @@
    - + @@ -38,11 +38,11 @@ - {% for book in books|sort(attribute='title', reverse = False) %} + {% for book in books %}
    - + + + + + XPPL + + + + + + +
    +

    A Potential Collection

    +

    Every book ever requested by XPPL librarians:

    +
    +

    {% for pbook in pbooks %}{{ pbook.ptitle }} requested on: {{ pbook.time }} +

    {% endfor %} +
    + + + +
    + + diff --git a/app/templates/red_link.html b/app/templates/red_link.html index 3c81566..a4d6c5c 100755 --- a/app/templates/red_link.html +++ b/app/templates/red_link.html @@ -59,10 +59,12 @@ {% endfor %}
    - +
    + Category: {{ form.category(size=27, class="form-control") }}
    -
    Category: {{ form.category(size=27, class="form-control") }}
    +
    + Year published: {{ form.year_published(size=8, class="form-control") }}
    {{ form.file }} {{ form.upload }} diff --git a/app/views.py b/app/views.py index c32106f..163a448 100755 --- a/app/views.py +++ b/app/views.py @@ -10,10 +10,11 @@ from flask import Flask, Response, render_template, request, redirect, url_for, import json from sqlalchemy.sql.expression import func, select from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm -from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema +from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Potential from app.cover import get_cover from os import environ from flask_socketio import SocketIO, emit +from weasyprint import HTML import datetime import time import autocomplete @@ -128,6 +129,15 @@ def remove_book_by_id(id): flash("%s deleted from library" % (title)) return redirect(url_for('show_books')) +@app.route('/potential') +def htmlpdf(): + paragraphs= ['test title'] + template = 'app/templates/potential_pdf.html' + html_string = render_template('potential_pdf.html', paragraphs=paragraphs) + html = HTML(string=html_string) + html.write_pdf(target='app/uploads/potential2.pdf'); + return render_template('potential_pdf.html', paragraphs=paragraphs) + @app.route('/books//edit', methods=['POST', 'GET']) def edit_book_by_id(id): book_to_edit = Book.query.filter_by(id=id).first() @@ -223,13 +233,20 @@ def add_book(): flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) #if upload without file -> wishform, with potential PDF if upload_form.wish.data: - #TO DO: make pdf generator - #file = open('app/uploads/potential.pdf') - #filename = 'potential.pdf' - #file_extension = '.pdf' - filename = '' - file_extension = '' - cover = '' + #pdf generator + filename = 'potential.pdf' + file_extension = '.pdf' + cover= '' + ptitle = upload_form.title.data + pbook = Potential(ptitle) + db.session.add(pbook) + db.session.commit() + pbooks = Potential.query.all() + template = 'app/templates/potential_pdf.html' + html_string = render_template('potential_pdf.html', pbooks = pbooks) + html = HTML(string=html_string) + html.write_pdf(target='app/uploads/potential.pdf'); + print ('potential_pdf') book = Book(title, filename, cover, file_extension, category,year_published) db.session.add(book) diff --git a/app/views_2.py b/app/views_2.py deleted file mode 100755 index edf46ac..0000000 --- a/app/views_2.py +++ /dev/null @@ -1,539 +0,0 @@ -""" -Flask Documentation: http://flask.pocoo.org/docs/ -Jinja2 Documentation: http://jinja.pocoo.org/2/documentation/ -Werkzeug Documentation: http://werkzeug.pocoo.org/documentation/ -This file creates your application. -""" - -from app import app, db, socketio, DOMAIN -from flask import Flask, Response, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort -import json -from sqlalchemy.sql.expression import func, select -from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm -from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema -from app.cover import get_cover -from os import environ -from flask_socketio import SocketIO, emit -import datetime -import time -import autocomplete -import sys -from csv import DictWriter -import io - -import os -from werkzeug.utils import secure_filename - -# import sqlite3 -ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'chm', 'mobi']) - -author_schema = AuthorSchema() -authors_schema = AuthorSchema(many=True) -book_schema = BookSchema() -books_schema = BookSchema(many=True) -stack_schema = StackSchema() -stacks_schema = StackSchema(many=True) -chat_schema = ChatSchema() -chats_schema = ChatSchema(many=True) - -def allowed_file(filename): - return '.' in filename and \ - filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS -### -# Routing for your application. -### - -@app.route('/', methods= ['POST','GET']) -def home(): - chat_form = ChatForm() - chat_messages = db.session.query(Chat).all() - - # if request.method == 'POST': - # if chat_form.validate_on_submit(): - # message = chat_form.message.data - # msg = Chat(message) - # db.session.add(msg) - # db.session.commit() - - return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username="librarian") - -@app.route('/hello/') -def hello(name): - return "Hello " + name - -@app.route('/about/') -def about(): - """Render the website's about page.""" - return render_template('about.html', name="Mary Jane") - -@app.route('/uploads/') -def uploaded_file(filename): - return send_from_directory(app.config['UPLOAD_FOLDER'], - filename) - -@app.route('/uploads/cover/') -def uploaded_file_cover(filename): - return send_from_directory(app.config['UPLOAD_FOLDER_COVER'], - filename) - -@app.route('/updates', methods=['POST', 'GET']) -def get_updates(): - userin = UserIns.query.filter_by(title="lastViewed").first() - allbooks = db.session.query(Book).all() - id = len(allbooks) - latest_upload = allbooks[-1] - return "This is the XPPL ~ Library XPUB ~ Updates / / / / / / / Last viewed: " + userin.info + " / / / / / / / " + str(len(allbooks)) + " Books online "+ " / / / / / / / " + "Latest upload: " + latest_upload.title - -@app.route('/scape', methods=['POST', 'GET']) -def scape(): - if request.method == 'POST': - data = request.form - book = Book.query.get(data['id']) - print(book.scapeX) - book.scapeX = data['x'] - book.scapeY = data['y'] - db.session.commit() - books = db.session.query(Book).all() # or you could have used User.query.all() - return render_template('scape.html', books=books) - - -@app.route('/books_grid') -def show_books_grid(): - books = db.session.query(Book).all() # or you could have used User.query.all() - return render_template('show_books_grid.html', books=books) - -@app.route('/books/') -def show_book_by_id(id): - book = Book.query.get(id) - userin = UserIns.query.filter_by(title="lastViewed").first() - if userin != None: - userin.info = book.title - db.session.commit() - else: - user_info = UserIns("lastViewed", book.title) - db.session.add(user_info) - db.session.commit() - if not book: - return render_template('red_link.html', id=id) - else: - return render_template('show_book_detail.html', book=book) - - -@app.route('/books//delete', methods=['POST', 'GET']) -def remove_book_by_id(id): - book_to_edit = Book.query.filter_by(id=id).first() - title = book_to_edit.title - Book.query.filter_by(id=id).delete() - #author_table = Author.query.filter_by(book_id=book_to_edit.id).delete() - db.session.commit() - flash("%s deleted from library" % (title)) - return redirect(url_for('show_books')) - -@app.route('/books//edit', methods=['POST', 'GET']) -def edit_book_by_id(id): - book_to_edit = Book.query.filter_by(id=id).first() - user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published) - - if request.method == 'POST': - if user_form.validate_on_submit(): - # on submit, check fields - title = user_form.title.data - input_authors = user_form.author.data - category = user_form.category.data - year_published = user_form.year_published.data - if year_published=="": - year_published = None - book = Book.query.filter_by(id=id).first() - book.title = title - book.category = category - book.year_published = year_published - - #authors update - book.authors.clear() - for i, author in enumerate(input_authors): - author_name = author.get("author_name") - if author_name: - a = db.session.query(Author).filter_by(author_name=author_name).first() - if a == None: - a = Author(author_name=author_name) - db.session.add(a) - book.authors.append(a) - - # editing / uploading new file - if user_form.file.data: - file = request.files['file'] - if file.filename == '': - flash('No selected file') - return redirect(request.url) - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - allbooks = db.session.query(Book).all() - id = book.id - new_filename = str(id) +"_"+ filename - fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) - name, file_extension = os.path.splitext(new_filename) - file.save(fullpath) - book.cover = get_cover(fullpath, name) - book.file = new_filename - else: - flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) - - db.session.commit() - flash("%s updated" % (title)) - return redirect(url_for('show_book_by_id', id=id)) - - return render_template('edit_book_detail.html', book=book_to_edit, form=user_form) - - -@app.route('/add-book', methods=['POST', 'GET']) -def add_book(): - upload_form = UploadForm() - - if request.method == 'POST': - if upload_form.validate_on_submit(): - #get data from form - title = upload_form.title.data - authors = upload_form.author.data - category = upload_form.category.data - year_published = upload_form.year_published.data - if year_published=="": - year_published = None - - #if upload with file - if upload_form.upload.data: - # check if the post request has the file part - if 'file' not in request.files: - flash('No file part') - return redirect(request.url) - file = request.files['file'] - # if user does not select file, browser also - # submit a empty part without filename - if file.filename == '': - flash('No selected file') - return redirect(request.url) - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - allbooks = db.session.query(Book).all() - id = len(allbooks)+1 - new_filename = str(id) +"_"+ filename - fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename) - name, file_extension = os.path.splitext(new_filename) - file.save(fullpath) - cover = get_cover(fullpath, name) - else: - flash('allowed file formats: %s' % ALLOWED_EXTENSIONS) - #if upload without file -> wishform, with potential PDF - if upload_form.wish.data: - #TO DO: make pdf generator - #file = open('app/uploads/potential.pdf') - #filename = 'potential.pdf' - #file_extension = '.pdf' - filename = '' - file_extension = '' - cover = '' - - book = Book(title, filename, cover, file_extension, category,year_published) - db.session.add(book) - for author in authors: - author_name = author.get("author_name") - if author_name: - a = db.session.query(Author).filter_by(author_name=author_name).first() - if a == None: - a = Author(author_name=author_name) - db.session.add(a) - book.authors.append(a) - db.session.commit() - - flash("%s added to the library" % (title)) - return redirect(url_for('show_books')) - - flash_errors(upload_form) - return render_template('add_book.html', form=upload_form) - - -# Flash errors from the form if validation fails -def flash_errors(form): - for field, errors in form.errors.items(): - for error in errors: - flash(u"Error in the %s field - %s" % ( - getattr(form, field).label.text, - error - )) - -#Authors - -@app.route('/authors/') -def show_author_by_id(id): - author = Author.query.get(id) - if not author: - abort (404) - else: - return render_template('show_author_detail.html', author=author) - - -@app.route('/authors//edit', methods=['POST', 'GET']) -def edit_author_by_id(id): - return "Ask the programmer." - -##stacks - -@app.route('/stacks') -def show_stacks(): - stacks = db.session.query(Stack).all() - return render_template('show_stacks.html', stacks=stacks) - -@app.route('/stacks/add_stack', methods=['POST', 'GET']) -def add_stack(): - form = StackForm() - stacks = db.session.query(Stack).all() - - if form.validate_on_submit(): - stack_name = form.stack_name.data - stack_description = form.stack_description.data - stack = Stack(stack_name, stack_description) - if form.stack_name.data: - stack = Stack(stack_name, stack_description) - db.session.add(stack) - stacks = db.session.query(Stack).all() - return redirect(url_for('show_stacks')) - flash("%s stack created" % (stack_name)) - return render_template('add_stack.html', stacks=stacks, form=form) - -@app.route('/stacks/tab/', methods=['POST', 'GET']) -def show_stack_in_tab(id): - return show_stack_by_id(id, is_tab=True) - - -@app.route('/stacks/', methods=['POST', 'GET']) -def show_stack_by_id(id, is_tab=False): - - stack = Stack.query.get(id) - if not stack: - abort (404) - else: - if is_tab == False: - return render_template('show_stack_detail.html', stack=stack) - else: - return render_template('show_stack_detail_tab.html', stack=stack) - -@app.route('/stacks//delete', methods=['POST', 'GET']) -def remove_stack_by_id(id): - Stack.query.filter_by(id=id).delete() - db.session.commit() - return redirect(url_for('show_stacks')) - -@app.route('/stacks//edit', methods=['POST', 'GET']) -def edit_stack_by_id(id): - stack = Stack.query.filter_by(id=id).first() - form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description) - - if request.method == 'POST': - if form.validate_on_submit(): - stack_name = form.edit_stack_name.data - stack_description = form.edit_stack_description.data - stack.stack_name = stack_name - stack.stack_description = stack_description - db.session.commit() - - return redirect(url_for('show_stack_by_id', id=id)) - return render_template('edit_stack_detail.html', stack=stack, form=form) - -#@app.route('/stacks//remove/', methods=['POST', 'GET']) -#def remove_from_stack(bookid, stackid): -# import ipdb; ipdb.set_trace() -# book = Book.query.get(id) -# stack = Stack.query.get(id) - -# stack = db.session.query(Stack).join(stack.books).filter_by(stackid=stackid) -# stack.books.delete(book) -# db.session.commit() -# return render_template('show_book_by_id.html', stackid=stack.id, bookid=book.id) - -@app.route('/add_to_stack/', methods=['GET', 'POST']) -def add_to_stack(id): - stacks = db.session.query(Stack).all() - add_form = AddtoStackForm(request.form) - add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks] - if request.method == 'GET': - book = Book.query.get(id) - return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form) - else: - stack = Stack.query.get(int(add_form.select_stack.data)) - book = Book.query.get(id) - stack.books.append(book) - db.session.commit() - return render_template('show_stack_detail.html', stack=stack) - -## search - -@app.route('/books', methods= ['POST','GET']) -def show_books(): - books = db.session.query(Book).all() - search = SearchForm(request.form) - if request.method == 'POST': - return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) - - return render_template('show_books.html', books=books, form=search) - -@app.route('/search///', methods=['POST', 'GET']) -def search_results(searchtype, query): - search = SearchForm(request.form) - random_order=Book.query.order_by(func.random()).limit(10) - results=Book.query.filter(Book.title.contains(query)) - - if searchtype == 'Title': - results=Book.query.filter(Book.title.contains(query)) - - if searchtype == 'Category': - results=Book.query.filter(Book.category.contains(query)) - - if searchtype== 'Author': - results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)) - - if searchtype== 'Stack': - results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)) - - if searchtype== 'All': - # results=Book.query.whoosh_search(query) - results=Book.query.filter(Book.title.contains(query)) - results=results.union(Book.query.filter(Book.category.contains(query))) - results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query))) - results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))) - - if results.count() == 0: - upload_form = UploadForm(title= query, author='') - return render_template('red_link.html', form=upload_form, title=query) - - if request.method == 'POST': - query = search.search.data - results = [] - return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) - - count = results.count() - whole = Book.query.count() - percentage = float(count / whole * 100) - return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) - - -## Search - autocomplete -autocomplete_suggestions = [] - -@app.route('/autocomplete_suggestions', methods=['GET', 'POST']) -def test1(): - if request.method == 'POST': - autocomplete.load() - query = request.form['search'] - query_tokenized = query.lower().split() - print(query_tokenized) - word_1 = query_tokenized[-2] - word_2 = query_tokenized[-1] - #print(word_1) - autocomplete_output = autocomplete.predict(word_1 , word_2) - autocomplete_suggestions.clear() - for suggestion, score in autocomplete_output: - autocomplete_suggestions.append(suggestion) - - print(autocomplete_suggestions) - - return Response(json.dumps(autocomplete_suggestions), mimetype='application/json') - - - - - -@app.route('/export/csv', methods=['GET']) -def export_csv (): - output = io.StringIO() - fieldnames = ['title', 'authors'] - csv = DictWriter(output,fieldnames) - csv.writeheader() - # for i in range(10): - # csv.writerow({'ID': i, 'fruit': "Tomato"}) - for book in Book.query.order_by("title"): - authors = ", ".join([x.author_name for x in book.authors]) - csv.writerow({"title": book.title, "authors": authors}) - resp = Response(output.getvalue(), mimetype="text/plain") - # resp.headers["Content-Disposition"] = "attachment;filename=export.csv" - return resp -### -# The API -### - -@app.route('/api/books', methods=['GET']) -def get_books(): - books = Book.query.all() - data = books_schema.dump(books) - #print(errors) - return jsonify({'books': data}) - -@app.route('/api/books/', methods=['GET']) -def get_book_by_id(id): - book = Book.query.get(id) - data = book_schema.dump(book) - if not data: - return jsonify({"message": "Book could not be found."}), 400 - else: - return jsonify({'book': data }) - -@app.route('/api/chats', methods=['GET']) -def get_chat(): - chats = Chat.query.all() - data = chats_schema.dump(chats) - #print(errors) - return jsonify({'chat': data}) - - - -### -# The functions below should be applicable to all Flask apps. -### - -@app.route('/.txt') -def send_text_file(file_name): - """Send your static text file.""" - file_dot_text = file_name + '.txt' - return app.send_static_file(file_dot_text) - - -@app.after_request -def add_header(response): - """ - Add headers to both force latest IE rendering engine or Chrome Frame, - and also to cache the rendered page for 10 minutes. - """ - response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1' - response.headers['Cache-Control'] = 'public, max-age=600' - return response - - -@app.errorhandler(404) -def page_not_found(error): - """Custom 404 page.""" - return render_template('404.html'), 404 - -@socketio.on('new_message') -def new_message(message): - # Send message to alls users - print("new message") - emit('channel-' + str(message['channel']), { - 'username': message['username'], - 'text': message['text'], - 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M")) - }, - broadcast=True - ) - # Save message - my_new_chat = Chat( - message=message['text'] - ) - db.session.add(my_new_chat) - try: - db.session.commit() - except: - db.session.rollback() - - - -if __name__ == '__main__': - socketio.run(app) - #app.run(debug=True,host="0.0.0.0",port="8080") From 5fbdb066650da80264ee219b74034db7961895df Mon Sep 17 00:00:00 2001 From: nberting Date: Sat, 9 Jun 2018 18:13:18 +0200 Subject: [PATCH 17/31] updated styling potential pdfs --- app/static/css/style.css | 5 +++++ app/templates/potential_pdf.html | 27 ++++++++++----------------- app/templates/red_link.html | 3 ++- app/templates/results.html | 8 ++++---- app/templates/show_books.html | 4 ++-- app/views.py | 4 +++- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/static/css/style.css b/app/static/css/style.css index ec32671..fc8faab 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -15,6 +15,11 @@ a{ text-decoration: none; color: black; } +a:hover{ + + text-decoration: underline; + color: #0000FF; +} .navigation ul{ list-style-type: none; diff --git a/app/templates/potential_pdf.html b/app/templates/potential_pdf.html index 4450325..1800cf6 100644 --- a/app/templates/potential_pdf.html +++ b/app/templates/potential_pdf.html @@ -13,27 +13,17 @@ XPPL - - - - -
    -

    A Potential Collection

    -

    Every book ever requested by XPPL librarians:

    -
    -

    {% for pbook in pbooks %}{{ pbook.ptitle }} requested on: {{ pbook.time }} +

    +

    Potential Books

    + +

    Archiving the gaps in the XPPL library:

    +
    +

    {% for pbook in pbooks %}"{{ pbook.ptitle }}" requested on: {{ pbook.time|string|truncate(17,true," ")}}

    {% endfor %}
    - - - -
    -
    Title AuthorFiletypeYear Category Stack Add to stack{{ book.fileformat }}{{ book.year_published }} {{ book.category}} {% for stack in book.stacks %} @@ -78,7 +78,7 @@ Cover Title AuthorFiletypeYear Category Stack Add to stack{{ book.fileformat }}{{ book.year_published }} {{ book.category}} {% for stack in book.stacks %} diff --git a/app/templates/show_books.html b/app/templates/show_books.html index 1b8a369..ac9a819 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -32,7 +32,7 @@ Cover Title AuthorFiletypeYear Category Stack Add to a stack{{ book.fileformat }}{{ book.year_published }} {{ book.category}} {% for stack in book.stacks %} diff --git a/app/views.py b/app/views.py index 565f123..ec86fd7 100755 --- a/app/views.py +++ b/app/views.py @@ -430,8 +430,10 @@ def search_results(searchtype, query, viewby): # results=Book.query.whoosh_search(query) results=Book.query.filter(Book.title.contains(query)) results=results.union(Book.query.filter(Book.category.contains(query))) + results=results.union(Book.query.filter(Book.year_published.contains(query))) results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query))) - results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))).order_by(Book.title) + results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))) + results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_description.contains(query))).order_by(Book.title) if results.count() == 0: upload_form = UploadForm(title= query, author='') From 0696c94a147032a0cbbd38fed201166c7f656d5e Mon Sep 17 00:00:00 2001 From: nberting Date: Sat, 9 Jun 2018 19:27:55 +0200 Subject: [PATCH 18/31] updates red link page --- app/static/css/style.css | 1 + app/templates/red_link.html | 65 ++++++++++++++++++++++++++++-- app/templates/results.html | 7 ++-- app/templates/results_grid.html | 9 ++--- app/templates/show_books.html | 2 +- app/templates/show_books_grid.html | 2 +- app/views.py | 10 ++--- 7 files changed, 75 insertions(+), 21 deletions(-) diff --git a/app/static/css/style.css b/app/static/css/style.css index fc8faab..1753ba4 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -82,6 +82,7 @@ border-spacing:0; /* Removes the cell spacing via CSS */ .library_table th{ font-size: 20px; cursor: pointer; +background-color: #fafafa; } th.headerSortUp{ diff --git a/app/templates/red_link.html b/app/templates/red_link.html index c15e812..22f101d 100755 --- a/app/templates/red_link.html +++ b/app/templates/red_link.html @@ -37,7 +37,7 @@
    -

    Add this potential book:

    +

    Add this book:

    {{ form.csrf_token }} @@ -60,17 +60,76 @@
    -
    +
    Category: {{ form.category(size=27, class="form-control") }}

    Year published: {{ form.year_published(size=8, class="form-control") }} +

    {{ form.file }} {{ form.upload }} {{ form.wish }}
    -

    go back home

    +
    +
    +
    +

    More potential books

    + + + + + + + + + + + + + + {% for book in books %} + + + + + + + + + + + + {% endfor %} + +
    + + + + {{ book.title }} {% for author in book.authors %} + +
  • {{ author.author_name }}
  • + + {% endfor %}
    {{ book.year_published }}{{ book.category}} + {% for stack in book.stacks %} + +
  • + + {{ stack.stack_name }} +
  • + + {% endfor %} + + +
    + + ===> +
    + {% endblock %} diff --git a/app/templates/results.html b/app/templates/results.html index 9ba0595..8e88967 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -53,7 +53,7 @@
  • {{ author.author_name }}
  • {% endfor %} - {{ book.year_published }} + {{ book.year_published or '––'}} {{ book.category}} {% for stack in book.stacks %} @@ -69,8 +69,7 @@
    -
    -
    +

    Other books

    @@ -94,7 +93,7 @@
  • {{ author.author_name }}
  • {% endfor %} - + +{{ book.title }} ({{ book.year_published or '?' }})
    {% for author in book.authors %} @@ -53,8 +53,7 @@ -
    -
    +

    Other books

    @@ -67,7 +66,7 @@

    +{{ book.title }} ({{ book.year_published or '?' }})
    {% for author in book.authors %} diff --git a/app/templates/show_books.html b/app/templates/show_books.html index ac9a819..fc4c1da 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -56,7 +56,7 @@
  • {{ author.author_name }}
  • {% endfor %} -
    + +{{ book.title }} ({{ book.year_published or '–' }})
    {% for author in book.authors %} diff --git a/app/views.py b/app/views.py index ec86fd7..1cf4a11 100755 --- a/app/views.py +++ b/app/views.py @@ -387,11 +387,6 @@ def show_books(): if request.method == 'POST': newmsg = 'searched for: ' + search.search.data - # message = search.search.data - # newmessage = Chat(message) - # db.session.add(newmessage) - # db.session.commit() - # Send search to socket chat socketio.emit('channel-' + str(1), { 'username': 'Search form', 'text': search.search.data, @@ -410,7 +405,7 @@ def show_books(): @app.route('/search///', methods=['POST', 'GET']) def search_results(searchtype, query, viewby): search = SearchForm(request.form, search=query) - random_order=Book.query.order_by(func.random()).limit(14) + random_order=Book.query.all() results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) viewby = view[-1] @@ -436,8 +431,9 @@ def search_results(searchtype, query, viewby): results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_description.contains(query))).order_by(Book.title) if results.count() == 0: + books = Book.query.filter(Book.file.like('potential.pdf')) upload_form = UploadForm(title= query, author='') - return render_template('red_link.html', form=upload_form, title=query) + return render_template('red_link.html', form=upload_form, title=query, books=books) count = results.count() whole = Book.query.count() From 2bc81e7d59c8ef0834a75175c004bd7f5604ff7c Mon Sep 17 00:00:00 2001 From: nberting Date: Sat, 9 Jun 2018 22:05:47 +0200 Subject: [PATCH 19/31] added message option into book database, upload form, edit form --- app/forms.py | 2 ++ app/models.py | 5 +++- app/static/css/style.css | 41 +++++++++++++++++++++++++++-- app/static/js/app.js | 26 ++++++++++++++++++ app/templates/add_book.html | 4 +++ app/templates/edit_book_detail.html | 19 +++++++------ app/templates/show_book_detail.html | 21 ++++++++++----- app/views.py | 14 +++------- import_csv.py | 2 +- 9 files changed, 105 insertions(+), 29 deletions(-) diff --git a/app/forms.py b/app/forms.py index f1704ab..6089ca9 100755 --- a/app/forms.py +++ b/app/forms.py @@ -19,6 +19,7 @@ class UploadForm(FlaskForm): file = FileField() upload = SubmitField(label='Upload') wish = SubmitField(label='''I don't have the file, but wish I did.''') + message = StringField('message', default=None) class EditForm(FlaskForm): title = StringField('title', validators=[InputRequired()]) @@ -26,6 +27,7 @@ class EditForm(FlaskForm): category = StringField('category', validators=[InputRequired()]) year_published = StringField('year published', [validators.Length(max=4)],default=None) file = FileField() + message = StringField('message') class ChatForm(FlaskForm): message = StringField('message', validators=[InputRequired()]) diff --git a/app/models.py b/app/models.py index dd80db8..2f6f119 100755 --- a/app/models.py +++ b/app/models.py @@ -33,8 +33,10 @@ class Book(db.Model): backref=db.backref('books', lazy=True)) scapeX = db.Column(db.Numeric(10,2)) scapeY = db.Column(db.Numeric(10,2)) + message = db.Column(db.String(1000)) + - def __init__(self, title, file, cover, fileformat, category, year_published): + def __init__(self, title, file, cover, fileformat, category, year_published, message): self.title = title self.file = file self.cover = cover @@ -43,6 +45,7 @@ class Book(db.Model): self.year_published = year_published self.scapeX = 0 self.scapeY = 0 + self.message = message def __repr__(self): diff --git a/app/static/css/style.css b/app/static/css/style.css index 1753ba4..2ea3389 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -82,7 +82,6 @@ border-spacing:0; /* Removes the cell spacing via CSS */ .library_table th{ font-size: 20px; cursor: pointer; -background-color: #fafafa; } th.headerSortUp{ @@ -352,7 +351,7 @@ box-sizing: border-box; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-gap: 2px; - align-items: center; + align-items: top; justify-items: center; } } @@ -374,3 +373,41 @@ box-sizing: border-box; .gridbox:hover{ opacity: 0.5; } + +/* The Modal (background) */ +.modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 1; /* Sit on top */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ +} + +/* Modal Content/Box */ +.modal-content { + background-color: #fefefe; + margin: 15% auto; /* 15% from the top and centered */ + padding: 20px; + border: 1px solid #888; + width: 40%; /* Could be more or less, depending on screen size */ +} + +/* The Close Button */ +.close { + color: red; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} diff --git a/app/static/js/app.js b/app/static/js/app.js index 460c9ee..55d72bb 100755 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -253,3 +253,29 @@ $('#search').on("input", function() { }); }); + +// Get the modal +var modal = document.getElementById('myModal'); + +// Get the button that opens the modal +var btn = document.getElementById("myBtn"); + +// Get the element that closes the modal +var span = document.getElementsByClassName("close")[0]; + +// When the user clicks on the button, open the modal +btn.onclick = function() { + modal.style.display = "block"; +} + +// When the user clicks on (x), close the modal +span.onclick = function() { + modal.style.display = "none"; +} + +// When the user clicks anywhere outside of the modal, close it +window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } +} diff --git a/app/templates/add_book.html b/app/templates/add_book.html index b9dff17..35c5c6b 100755 --- a/app/templates/add_book.html +++ b/app/templates/add_book.html @@ -41,6 +41,10 @@
    Year published: {{ form.year_published(size=8, class="form-control") }}
    +
    +
    + Add a message for future readers: {{ form.message(size=150, class="form-control") }} +

    {{ form.file }} {{ form.upload }} diff --git a/app/templates/edit_book_detail.html b/app/templates/edit_book_detail.html index 4299e95..2fa5268 100755 --- a/app/templates/edit_book_detail.html +++ b/app/templates/edit_book_detail.html @@ -31,19 +31,22 @@ {% endfor %}
    {{ book.year_published }}{{ book.year_published or '––'}} {{ book.category}} {% for stack in book.stacks %} diff --git a/app/templates/results_grid.html b/app/templates/results_grid.html index 5b2a1ec..d043c4a 100644 --- a/app/templates/results_grid.html +++ b/app/templates/results_grid.html @@ -30,7 +30,7 @@ {% endif %} {% endwith %} -
    +
    {% for book in books|sort(attribute='title', reverse = False) %} @@ -41,7 +41,7 @@

    -{{ book.title }}
    -{{ book.title }} {{ book.year_published }}{{ book.year_published or '––'}} {{ book.category}} {% for stack in book.stacks %} diff --git a/app/templates/show_books_grid.html b/app/templates/show_books_grid.html index 4384ee8..cc2c2f3 100644 --- a/app/templates/show_books_grid.html +++ b/app/templates/show_books_grid.html @@ -38,7 +38,7 @@

    -{{ book.title }}

    -
    - {{ form.category.label }} {{ form.category(size=20, +
    + Category: {{ form.category(size=20, class="form-control") }} -

    -
    - {{ form.year_published.label }} {{ form.year_published(size=4, class="form-control") }} +
    +
    + Year published: {{ form.year_published(size=8, class="form-control") }}
    -
    +
    Current file: {{ book.file }}
    -
    - +
    Upload new file: {{form.file}} +
    +
    + + If uploading, write a new message: {{form.message(size=150, class="form-control") }}

    diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index a8b29d3..79252d3 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -16,17 +16,26 @@

    Included in stack(s):

    - download {{ book.fileformat }} -
    -
    - edit + + + + +
    + {% endblock %} + + diff --git a/app/views.py b/app/views.py index 1cf4a11..199b264 100755 --- a/app/views.py +++ b/app/views.py @@ -129,19 +129,10 @@ def remove_book_by_id(id): flash("%s deleted from library" % (title)) return redirect(url_for('show_books')) -@app.route('/potential') -def htmlpdf(): - paragraphs= ['test title'] - template = 'app/templates/potential_pdf.html' - html_string = render_template('potential_pdf.html', paragraphs=paragraphs) - html = HTML(string=html_string) - html.write_pdf(target='app/uploads/potential2.pdf'); - return render_template('potential_pdf.html', paragraphs=paragraphs) - @app.route('/books//edit', methods=['POST', 'GET']) def edit_book_by_id(id): book_to_edit = Book.query.filter_by(id=id).first() - user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published) + user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published, message= book_to_edit.message) if request.method == 'POST': if user_form.validate_on_submit(): @@ -204,6 +195,7 @@ def add_book(): title = upload_form.title.data authors = upload_form.author.data category = upload_form.category.data + message = upload_form.message.data year_published = upload_form.year_published.data if year_published=="": year_published = None @@ -248,7 +240,7 @@ def add_book(): html.write_pdf(target='app/uploads/potential.pdf'); print ('potential_pdf') - book = Book(title, filename, cover, file_extension, category,year_published) + book = Book(title, filename, cover, file_extension, category, year_published, message) db.session.add(book) for author in authors: author_name = author.get("author_name") diff --git a/import_csv.py b/import_csv.py index b43447d..bca2b12 100644 --- a/import_csv.py +++ b/import_csv.py @@ -20,7 +20,7 @@ with open(args.csv) as f: print ('get_cover', fullpath, name) cover = get_cover(fullpath, name) - book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None) + book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None, '') db.session.add(book) authors = row['Author'].split(',') From d769aba95ff93921d0c63a7ed44f2f7975162ebb Mon Sep 17 00:00:00 2001 From: Alice Date: Sun, 10 Jun 2018 11:12:59 +0200 Subject: [PATCH 20/31] ignored some files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 004b085..1651a7b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ __pycache__/ app/uploads/** !app/uploads/cover app/mydatabase.db +pyrqlite/ +whoosh/ +sqlalchemy-rqlite/ +rqlite* From 22494aadd27c097c1ef207848b05e68ad0a18ecb Mon Sep 17 00:00:00 2001 From: nberting Date: Sun, 10 Jun 2018 11:16:47 +0200 Subject: [PATCH 21/31] styling buttons --- app/static/css/style.css | 10 +++++----- app/templates/show_book_detail.html | 18 ++++++++++-------- app/views.py | 2 ++ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/app/static/css/style.css b/app/static/css/style.css index 2ea3389..a7f6bbf 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -385,21 +385,21 @@ box-sizing: border-box; height: 100%; /* Full height */ overflow: auto; /* Enable scroll if needed */ background-color: rgb(0,0,0); /* Fallback color */ - background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ + background-color: rgba(0,0,0,0.5); /* Black w/ opacity */ } /* Modal Content/Box */ .modal-content { - background-color: #fefefe; + background-color: yellow; margin: 15% auto; /* 15% from the top and centered */ - padding: 20px; + padding: 15px; border: 1px solid #888; width: 40%; /* Could be more or less, depending on screen size */ } /* The Close Button */ .close { - color: red; + color: grey; float: right; font-size: 28px; font-weight: bold; @@ -407,7 +407,7 @@ box-sizing: border-box; .close:hover, .close:focus { - color: black; + color: red; text-decoration: none; cursor: pointer; } diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index 79252d3..ea6b6b9 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -21,18 +21,20 @@ {% endfor %}

    -
    - - - +{% if book.file %} + + "{{book.message or 'Happy reading.'}}"
    +

    >>>> Link to file <<<<

    +
    +{% else %} +{% endif %} + + +
    diff --git a/app/views.py b/app/views.py index 199b264..27cb5fa 100755 --- a/app/views.py +++ b/app/views.py @@ -141,12 +141,14 @@ def edit_book_by_id(id): input_authors = user_form.author.data category = user_form.category.data year_published = user_form.year_published.data + message = user_form.message.data if year_published=="": year_published = None book = Book.query.filter_by(id=id).first() book.title = title book.category = category book.year_published = year_published + book.message = message #authors update book.authors.clear() From b8a6c86a13502c961e60a217077a15cff3c408ce Mon Sep 17 00:00:00 2001 From: nberting Date: Sun, 10 Jun 2018 12:29:09 +0200 Subject: [PATCH 22/31] reverse query results and some new links on show_book_detail --- app/templates/results.html | 5 +++-- app/templates/results_grid.html | 5 ++--- app/templates/show_book_detail.html | 7 ++++++- app/views.py | 24 ++++++++++++++++++------ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/templates/results.html b/app/templates/results.html index 8e88967..296dc8e 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -31,7 +31,7 @@ {% endif %} {% endwith %} - +
    @@ -68,9 +68,10 @@
    +
    -

    Other books

    +

    More books

    diff --git a/app/templates/results_grid.html b/app/templates/results_grid.html index d043c4a..228f7db 100644 --- a/app/templates/results_grid.html +++ b/app/templates/results_grid.html @@ -30,7 +30,7 @@ {% endif %} {% endwith %} -
    +
    {% for book in books|sort(attribute='title', reverse = False) %} @@ -52,9 +52,8 @@ {% endfor %}
    -
    -

    Other books

    +

    More books

    diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index ea6b6b9..e7a0e77 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -35,7 +35,12 @@ - +

    +
    +{% if previousbook %} + < see the previous book added to XPPL:  {{ previousbook.title |truncate(40,True,'...') }} {% endif %} +{% if nextbook %} + see the next book added to XPPL:  {{ nextbook.title|truncate(40,True,'...')}} > {% endif %}
    {% endblock %} diff --git a/app/views.py b/app/views.py index 27cb5fa..2a97058 100755 --- a/app/views.py +++ b/app/views.py @@ -9,6 +9,7 @@ from app import app, db, socketio, DOMAIN from flask import Flask, Response, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort import json from sqlalchemy.sql.expression import func, select +from sqlalchemy.sql import except_ from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Potential from app.cover import get_cover @@ -105,6 +106,15 @@ def show_books_grid(): @app.route('/books/') def show_book_by_id(id): book = Book.query.get(id) + previousbook = Book.query.filter_by(id=id - 1).first() + nextbook = Book.query.filter_by(id=id + 1).first() + allbooks = db.session.query(Book).all() + edge = len(allbooks) + if id == 1: + previousbook = None + if id == edge: + nextbook = None + userin = UserIns.query.filter_by(title="lastViewed").first() if userin != None: userin.info = book.title @@ -116,7 +126,7 @@ def show_book_by_id(id): if not book: return render_template('red_link.html', id=id) else: - return render_template('show_book_detail.html', book=book) + return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook) @app.route('/books//delete', methods=['POST', 'GET']) @@ -399,8 +409,8 @@ def show_books(): @app.route('/search///', methods=['POST', 'GET']) def search_results(searchtype, query, viewby): search = SearchForm(request.form, search=query) - random_order=Book.query.all() results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) + allbooks = set(Book.query.all()) viewby = view[-1] if searchtype == 'Title': @@ -432,14 +442,16 @@ def search_results(searchtype, query, viewby): count = results.count() whole = Book.query.count() percentage = float(count / whole * 100) + fbooks = set(results) + books_all = allbooks - fbooks if search.listview.data: view.append('1') - return render_template('results.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) + return render_template('results.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) if search.grid.data: view.append('2') - return render_template('results_grid.html', books=results, form=search, query=query, books_all=random_order, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) + return render_template('results_grid.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) if request.method == 'POST': newmsg = 'searched for: ' + search.search.data @@ -464,10 +476,10 @@ def search_results(searchtype, query, viewby): return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) if viewby == '2': - return render_template('results_grid.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) + return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) else: - return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) + return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage) # ## Search - autocomplete From 93bf8a6b938f9a054c4bf39ef717c7d20d7cfdca Mon Sep 17 00:00:00 2001 From: nberting Date: Sun, 10 Jun 2018 17:41:44 +0200 Subject: [PATCH 23/31] extended info on add-book page --- app/static/css/style.css | 3 ++ app/templates/add_book.html | 48 +++++++++++++++++++++++++---- app/templates/show_book_detail.html | 39 +++++++++++++++++------ app/views.py | 13 +++++++- 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/app/static/css/style.css b/app/static/css/style.css index a7f6bbf..6db020c 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -83,6 +83,9 @@ border-spacing:0; /* Removes the cell spacing via CSS */ font-size: 20px; cursor: pointer; } +.library_table td{ +padding: 5px; +} th.headerSortUp{ background-color: #E8E8E8!important; diff --git a/app/templates/add_book.html b/app/templates/add_book.html index 35c5c6b..cf5dac6 100755 --- a/app/templates/add_book.html +++ b/app/templates/add_book.html @@ -1,7 +1,9 @@ {% extends 'base.html' %} {% block main %} -
    +
    +
    +

    Add Book

    {% with messages = get_flashed_messages() %} {% if messages %} @@ -33,25 +35,59 @@ {% endfor %}
    -
    -
    Category: {{ form.category(size=27, class="form-control") }}

    Year published: {{ form.year_published(size=8, class="form-control") }}

    -
    - Add a message for future readers: {{ form.message(size=150, class="form-control") }} +
    + Add a message for future readers: {{ form.message(size=90, class="form-control") }}

    {{ form.file }} {{ form.upload }} {{ form.wish }}
    - +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Titles: {{ books_all }}
    Authors: {{ authors_all }}
    Categories: {{ categories|replace('[', '')|replace(']', '') }}
    Stacks: {{ stacks_all|replace('[', '')|replace(']', '') }}
    From the years: {{earliest}} –– {{latest}}
    Gaps in the collection: At least {{ books_potential }} potential books missing
    +
    {% endblock %} diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index e7a0e77..19e8c64 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -5,21 +5,40 @@

    {{ book.title }}

    -

    Year published: {{ book.year_published }}

    -

    Author(s):

      {% for author in book.authors %} -
    • {{ author.author_name }}
    • + + + + + + + + + + + + + + + + + + + + + + + + - {% endfor %}

      + +
      Year published: {{ book.year_published or '––'}}
      Author(s): {% for author in book.authors %} - {% endfor %}

      + {{ author.author_name }}
      -

      Category: {{ book.category }}

      - -

      Included in stack(s):

        {% for stack in book.stacks %} + {% endfor %}
      Category: {{ book.category }}
      Included in stack(s): {% for stack in book.stacks %}
    • {{ stack.stack_name }} -

      Remove from stack

      - +

      – Remove from stack{% endfor %}

    • {% if book.file %} diff --git a/app/views.py b/app/views.py index 2a97058..a55aa8a 100755 --- a/app/views.py +++ b/app/views.py @@ -200,6 +200,17 @@ def edit_book_by_id(id): @app.route('/add-book', methods=['POST', 'GET']) def add_book(): upload_form = UploadForm() + allbooks = db.session.query(Book).all() + books_all = len(allbooks) + allauthors = db.session.query(Author).all() + authors_all = len(allauthors) + stacks_all = [s.stack_name for s in db.session.query(Stack.stack_name)] + categories = [r.category for r in db.session.query(Book.category).distinct()] + allpotential = db.session.query(Book).filter(Book.file.contains('potential.pdf')).all() + books_potential = len(allpotential) + earliest = db.session.query(func.min(Book.year_published)).scalar() + latest = db.session.query(func.max(Book.year_published)).scalar() + if request.method == 'POST': if upload_form.validate_on_submit(): @@ -268,7 +279,7 @@ def add_book(): return redirect(url_for('show_books')) flash_errors(upload_form) - return render_template('add_book.html', form=upload_form) + return render_template('add_book.html', form=upload_form, books_all=books_all, authors_all=authors_all, categories=categories, stacks_all=stacks_all, books_potential=books_potential, earliest=earliest, latest=latest) # Flash errors from the form if validation fails From b369442e41b9538b0de9dcb01b6012f6fea6aee6 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 10 Jun 2018 19:22:24 +0200 Subject: [PATCH 24/31] views --- app/views.py | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/app/views.py b/app/views.py index 7c11b44..482d0f0 100755 --- a/app/views.py +++ b/app/views.py @@ -20,7 +20,7 @@ import time from csv import DictWriter, DictReader import io from sqlalchemy.inspection import inspect -import autocomplete +#import autocomplete import sys import os @@ -399,7 +399,7 @@ def show_instances(): @app.route('/books', methods= ['POST','GET']) def show_books(): - autocomplete.load() #Train markov model once, for autocomplete in search + #autocomplete.load() #Train markov model once, for autocomplete in search books = db.session.query(Book).all() search = SearchForm(request.form) @@ -435,28 +435,28 @@ def search_results(searchtype, query): return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query) ## Search - autocomplete -autocomplete_suggestions = [] -autocomplete.load() #Train markov model once, for autocomplete in search - -@app.route('/autocomplete_suggestions', methods=['GET', 'POST']) -def test1(): - if request.method == 'POST': - query = request.form['search'] - query_tokenized = query.lower().split() - print(query_tokenized) - word_1 = query_tokenized[-2] - word_2 = query_tokenized[-1] - #print(word_1) - autocomplete_output = autocomplete.predict(word_1 , word_2) - autocomplete_suggestions.clear() - for suggestion, score in autocomplete_output: - autocomplete_suggestions.append(suggestion) - - session['autocomplete_suggestions'] = str(autocomplete_suggestions) - - print(session['autocomplete_suggestions']) - - return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json') +#autocomplete_suggestions = [] +#autocomplete.load() #Train markov model once, for autocomplete in search + +# @app.route('/autocomplete_suggestions', methods=['GET', 'POST']) +# def test1(): +# if request.method == 'POST': +# query = request.form['search'] +# query_tokenized = query.lower().split() +# print(query_tokenized) +# word_1 = query_tokenized[-2] +# word_2 = query_tokenized[-1] +# #print(word_1) +# autocomplete_output = autocomplete.predict(word_1 , word_2) +# autocomplete_suggestions.clear() +# for suggestion, score in autocomplete_output: +# autocomplete_suggestions.append(suggestion) +# +# session['autocomplete_suggestions'] = str(autocomplete_suggestions) +# +# print(session['autocomplete_suggestions']) +# +# return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json') ## STACKS! From 2fb4013c7f0bf32a17df5fff32241d185648c90a Mon Sep 17 00:00:00 2001 From: Alice Date: Sun, 10 Jun 2018 19:33:50 +0200 Subject: [PATCH 25/31] added stack author --- app/forms.py | 5 +---- app/models.py | 7 +++++-- app/static/css/style.css | 4 ++++ app/templates/add_stack.html | 4 ++-- app/templates/show_books.html | 2 +- app/templates/show_stack_detail.html | 6 ++++++ app/templates/show_stack_detail_tab.html | 7 +++++++ app/templates/show_stacks.html | 4 ++-- app/views.py | 5 +++-- 9 files changed, 31 insertions(+), 13 deletions(-) diff --git a/app/forms.py b/app/forms.py index 6089ca9..ba1a8dd 100755 --- a/app/forms.py +++ b/app/forms.py @@ -36,6 +36,7 @@ class ChatForm(FlaskForm): class StackForm(FlaskForm): stack_name = StringField('Stack', validators=[InputRequired()]) stack_description = StringField('Description', validators=[InputRequired()]) + stack_author = StringField('Who made this', validators=[InputRequired()]) create = SubmitField(label='Create') class AddtoStackForm(FlaskForm): @@ -56,7 +57,3 @@ class SearchForm(FlaskForm): grid = SubmitField('Grid') listview = SubmitField('List') randomize = SubmitField('Order differently') - - - - diff --git a/app/models.py b/app/models.py index 2f6f119..6928cab 100755 --- a/app/models.py +++ b/app/models.py @@ -91,11 +91,14 @@ class Stack(db.Model): __tablename__ = 'stacks' id = db.Column(db.Integer, primary_key = True) stack_name = db.Column(db.String(50)) - stack_description = db.Column(db.String(500)) + stack_description = db.Column(db.String(1000)) + stack_author = db.Column(db.String(255)) - def __init__(self, stack_name, stack_description): + + def __init__(self, stack_name, stack_description, stack_author): self.stack_name = stack_name self.stack_description = stack_description + self.stack_author = stack_author def __repr__(self): return '' % self.stack_name diff --git a/app/static/css/style.css b/app/static/css/style.css index 6db020c..5573e85 100755 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -205,7 +205,11 @@ font-size: 12px; .ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { padding-bottom: 0; padding-right: .1em; border-right-width: 0; background-color: yellow !important; list-style-type: none;} .ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: left; width: 50em; font-size: 12px; list-style-type: none;} +#creator{ + font-size: 12px; + color: grey; +} #newstext{ width: 100%; diff --git a/app/templates/add_stack.html b/app/templates/add_stack.html index 0fe1e5f..c960eb3 100644 --- a/app/templates/add_stack.html +++ b/app/templates/add_stack.html @@ -16,14 +16,14 @@
    {% endif %} - {% endwith %} + {% endwith %}
    {{form.hidden_tag()}}
    {{ render_field(form.stack_name)}} {{ render_field(form.stack_description)}} - +{{ render_field(form.stack_author)}} diff --git a/app/templates/show_books.html b/app/templates/show_books.html index fc4c1da..8ab4350 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -63,7 +63,7 @@
  • - {{ stack.stack_name }} + {{ stack.stack_name }}
  • {% endfor %} diff --git a/app/templates/show_stack_detail.html b/app/templates/show_stack_detail.html index 0e2d293..f55d6a2 100644 --- a/app/templates/show_stack_detail.html +++ b/app/templates/show_stack_detail.html @@ -6,6 +6,12 @@

    {{ stack.stack_name }}

    {{ stack.stack_description }}

    +

    Created by: +{% if stack.stack_author == None %} anon

    +{% else %} + + {{ stack.stack_author }} +{% endif %}

    Books in this stack:

    {% for book in stack.books %} diff --git a/app/templates/show_stack_detail_tab.html b/app/templates/show_stack_detail_tab.html index afa9b30..cf32045 100644 --- a/app/templates/show_stack_detail_tab.html +++ b/app/templates/show_stack_detail_tab.html @@ -10,6 +10,13 @@

    {{ stack.stack_description }}

    +

    Created by: +{% if stack.stack_author == None %} anon

    +{% else %} + + {{ stack.stack_author }} +{% endif %} +

    Books in this stack: {% for book in stack.books %} diff --git a/app/templates/show_stacks.html b/app/templates/show_stacks.html index f1388a5..705d2f0 100644 --- a/app/templates/show_stacks.html +++ b/app/templates/show_stacks.html @@ -4,8 +4,6 @@

    Stacks

    These are all the stacks that have been built so far.

    -

    Add a new stack

    - @@ -26,6 +24,8 @@
    +

    Add a new stack

    +

    diff --git a/app/views.py b/app/views.py index a55aa8a..1ef7483 100755 --- a/app/views.py +++ b/app/views.py @@ -321,9 +321,10 @@ def add_stack(): if form.validate_on_submit(): stack_name = form.stack_name.data stack_description = form.stack_description.data - stack = Stack(stack_name, stack_description) + stack_author = form.stack_author.data + stack = Stack(stack_name, stack_description, stack_author) if form.stack_name.data: - stack = Stack(stack_name, stack_description) + stack = Stack(stack_name, stack_description, stack_author) db.session.add(stack) stacks = db.session.query(Stack).all() return redirect(url_for('show_stacks')) From 8fa8200c49007ab76e2576ef382cf5b7369dac47 Mon Sep 17 00:00:00 2001 From: nberting Date: Mon, 11 Jun 2018 12:00:48 +0200 Subject: [PATCH 26/31] new upload form questions --- app/forms.py | 15 ++++++++ app/models.py | 13 ++++++- app/templates/add_book.html | 59 ++++++++++++++++++++++++----- app/templates/show_book_detail.html | 57 ++++++++++++++++++++-------- app/views.py | 8 +++- import_csv.py | 2 +- 6 files changed, 126 insertions(+), 28 deletions(-) diff --git a/app/forms.py b/app/forms.py index 6089ca9..f314919 100755 --- a/app/forms.py +++ b/app/forms.py @@ -5,6 +5,8 @@ from wtforms import FieldList from wtforms import Form as NoCsrfForm from wtforms.fields import StringField, FormField, SubmitField, SelectField, RadioField from app.models import Book, BookSchema, Author, Stack, StackSchema +from wtforms.fields.html5 import DecimalRangeField + # - - - Forms - - - class AuthorForm(NoCsrfForm): @@ -20,6 +22,19 @@ class UploadForm(FlaskForm): upload = SubmitField(label='Upload') wish = SubmitField(label='''I don't have the file, but wish I did.''') message = StringField('message', default=None) + sameness = DecimalRangeField('sameness', default=0) + diversity = DecimalRangeField('diversity', default=0) + gender = DecimalRangeField('gender', default=50) + time = StringField('time', [validators.Length(max=5)],default=None) + choices = [('Student', 'Student'), + ('Librarian', 'Librarian'), + ('Pirate', 'Pirate'), + ('Teacher', 'Teacher'), + ('Institution', 'Institution'), + ('All of the above', 'All of the above'), + ('None of the above', 'None of the above')] + who = SelectField('', choices=choices, default='Student') + class EditForm(FlaskForm): title = StringField('title', validators=[InputRequired()]) diff --git a/app/models.py b/app/models.py index 2f6f119..8bdc1c2 100755 --- a/app/models.py +++ b/app/models.py @@ -26,7 +26,6 @@ class Book(db.Model): category = db.Column(db.String(255)) year_published = db.Column(db.Numeric(4,0)) description = db.Column(db.String(2500)) - html = db.Column(db.String(255)) authors = db.relationship('Author', secondary=authors,cascade="delete", lazy='subquery', backref=db.backref('books', lazy=True),passive_deletes=True) stacks = db.relationship('Stack', secondary=stacks, lazy='subquery', @@ -34,9 +33,14 @@ class Book(db.Model): scapeX = db.Column(db.Numeric(10,2)) scapeY = db.Column(db.Numeric(10,2)) message = db.Column(db.String(1000)) + sameness = db.Column(db.Numeric()) + diversity = db.Column(db.Numeric()) + gender = db.Column(db.Numeric()) + who = db.Column(db.String(255)) + time = db.Column(db.Numeric()) - def __init__(self, title, file, cover, fileformat, category, year_published, message): + def __init__(self, title, file, cover, fileformat, category, year_published, message, sameness, diversity, gender, who, time): self.title = title self.file = file self.cover = cover @@ -46,6 +50,11 @@ class Book(db.Model): self.scapeX = 0 self.scapeY = 0 self.message = message + self.sameness = sameness + self.diversity = diversity + self.gender = gender + self.who = who + self.time = time def __repr__(self): diff --git a/app/templates/add_book.html b/app/templates/add_book.html index cf5dac6..6326174 100755 --- a/app/templates/add_book.html +++ b/app/templates/add_book.html @@ -1,6 +1,21 @@ {% extends 'base.html' %} {% block main %} +{% from "_formhelpers.html" import render_field %} + + + +
    @@ -18,10 +33,10 @@ {% endwith %} {{ form.csrf_token }} -
    Title: {{ form.title (size=34, class="form-control") }}
    +
    Title:*
    {{ form.title (size=50, class="form-control") }}

    - Author(s): + Author(s):*
    @@ -29,19 +44,43 @@ {% for author in form.author %} - + {% endfor %}
    {{ author.author_name (size=40)}}{{ author.author_name (size=50)}}

    - Category: {{ form.category(size=27, class="form-control") }} -
    -
    - Year published: {{ form.year_published(size=8, class="form-control") }} -
    + Category:*
    {{ form.category(size=50, class="form-control") }}

    + Year published:
    {{ form.year_published(size=8, class="form-control") }}

    + +How different is this item to the rest of the collection? +Or is it more of the same?
    +{{ form.sameness(min=0, max=100, oninput="outputUpdate(value)") }}   +{{ form.sameness.data }} % different + +


    + +Check the bibliography. How diverse are the references in this book?
    +{{ form.diversity(min=0, max=100, oninput="outputUpdate2(value)") }}   +{{ form.diversity.data }} % diverse + +


    + +Check the writing. Who is speaking? Is the voice more often male or female?
    +{{ form.diversity(min=1, max=100, oninput="outputUpdate3(value)") }}   +{{ form.gender.data }} % female + +


    +Who are you? {{ render_field(form.who) }} + +


    +How much time have you spent with this item? +Include the time spent looking for it, and uploading it.
    +{{ form.time (size = 50, class="form-control")}} hours
    +


    +
    Add a message for future readers: {{ form.message(size=90, class="form-control") }}
    @@ -49,11 +88,12 @@ {{ form.file }} {{ form.upload }} {{ form.wish }} +
    - +
    @@ -90,4 +130,5 @@
    + {% endblock %} diff --git a/app/templates/show_book_detail.html b/app/templates/show_book_detail.html index 19e8c64..54deecd 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -3,17 +3,17 @@ {% block main %}

    {{ book.title }}

    - +
    +
    - - - - - - - - - +
    + + + + + + + @@ -21,21 +21,47 @@ + {{ author.author_name }}
    + {% endfor %} - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Year published: {{ book.year_published or '––'}}
    Author(s): {% for author in book.authors %} - - {{ author.author_name }}
    - - {% endfor %}
    Category: {{ book.category }}
    Included in stack(s): {% for stack in book.stacks %}
  • {{ stack.stack_name }}

    – Remove from stack{% endfor %}

  • Notes from uploader
    How different is this item to the rest of the collection?
    {{ book.sameness or '?'}} % different
    How diverse are the references in this book?
    {{ book.diversity or '?' }} % diverse
    Who is speaking?
    {{ book.gender or '?' }} % female
    Who is uploading
    {{ book.who or '?' }}
    How much time has been spent with this item?
    {{ book.time or '?' }}
    @@ -55,6 +81,7 @@

    +
    {% if previousbook %} < see the previous book added to XPPL:  {{ previousbook.title |truncate(40,True,'...') }} {% endif %} diff --git a/app/views.py b/app/views.py index a55aa8a..424abff 100755 --- a/app/views.py +++ b/app/views.py @@ -220,6 +220,12 @@ def add_book(): category = upload_form.category.data message = upload_form.message.data year_published = upload_form.year_published.data + sameness = upload_form.sameness.data + gender = upload_form.gender.data + diversity = upload_form.diversity.data + time = upload_form.time.data + who = upload_form.who.data + if year_published=="": year_published = None @@ -263,7 +269,7 @@ def add_book(): html.write_pdf(target='app/uploads/potential.pdf'); print ('potential_pdf') - book = Book(title, filename, cover, file_extension, category, year_published, message) + book = Book(title, filename, cover, file_extension, category, year_published, message, sameness, diversity, gender, who, time) db.session.add(book) for author in authors: author_name = author.get("author_name") diff --git a/import_csv.py b/import_csv.py index bca2b12..af53fbe 100644 --- a/import_csv.py +++ b/import_csv.py @@ -20,7 +20,7 @@ with open(args.csv) as f: print ('get_cover', fullpath, name) cover = get_cover(fullpath, name) - book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None, '') + book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None, None, None, None, None, None, None) db.session.add(book) authors = row['Author'].split(',') From c2e0bec1bede4ada76936b9bb86314fe3f35af73 Mon Sep 17 00:00:00 2001 From: Alice Date: Mon, 11 Jun 2018 12:19:49 +0200 Subject: [PATCH 27/31] fixed stack author --- import_csv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/import_csv.py b/import_csv.py index af53fbe..df681c0 100644 --- a/import_csv.py +++ b/import_csv.py @@ -39,7 +39,7 @@ with open(args.csv) as f: if stack: b = db.session.query(Stack).filter_by(stack_name=stack).first() if b == None: - b = Stack(stack_name=stack, stack_description=None) + b = Stack(stack_name=stack, stack_description=None, stack_author=None) db.session.add(b) book.stacks.append(b) From a4a214d488655f7032ba248e822ee3b40a2a3623 Mon Sep 17 00:00:00 2001 From: nberting Date: Mon, 11 Jun 2018 12:25:10 +0200 Subject: [PATCH 28/31] fixed stack link --- app/templates/show_books.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/templates/show_books.html b/app/templates/show_books.html index 8ab4350..b43fe3d 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -61,10 +61,7 @@ {% for stack in book.stacks %} -
  • - - {{ stack.stack_name }} -
  • +
  • {{ stack.stack_name }}
  • {% endfor %} From e7d7e9fe93590a87dc71d473033bfa38a3634f8b Mon Sep 17 00:00:00 2001 From: Alice Date: Mon, 11 Jun 2018 12:28:07 +0200 Subject: [PATCH 29/31] moved add stack link --- app/templates/header.html | 1 + app/templates/show_stacks.html | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/templates/header.html b/app/templates/header.html index 9f2406a..5fc33bf 100755 --- a/app/templates/header.html +++ b/app/templates/header.html @@ -4,6 +4,7 @@
  • Catalogue
  • Stacks
  • Add Book
  • +
  • Add Stack
  • About
  • diff --git a/app/templates/show_stacks.html b/app/templates/show_stacks.html index 705d2f0..51f1a17 100644 --- a/app/templates/show_stacks.html +++ b/app/templates/show_stacks.html @@ -23,8 +23,13 @@
    + +
    +
    +
    +
    +

    -

    Add a new stack



    From 8e872c1958ad8fd8282400be648fada1c7916249 Mon Sep 17 00:00:00 2001 From: nberting Date: Mon, 11 Jun 2018 13:08:22 +0200 Subject: [PATCH 30/31] updated potential pdf --- app/templates/potential_pdf.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/potential_pdf.html b/app/templates/potential_pdf.html index 1800cf6..866acec 100644 --- a/app/templates/potential_pdf.html +++ b/app/templates/potential_pdf.html @@ -15,10 +15,10 @@ XPPL -
    -

    Potential Books

    +
    +

    A Potential PDF

    -

    Archiving the gaps in the XPPL library:

    +

    Every book ever requested in the XPPL library:

    {% for pbook in pbooks %}"{{ pbook.ptitle }}" requested on: {{ pbook.time|string|truncate(17,true," ")}}

    {% endfor %} From 644ed7d6214217289daa4c6211ace9e72db37eb5 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 11 Jun 2018 14:03:34 +0200 Subject: [PATCH 31/31] fixed tabs after merge error --- app/templates/base.html | 1 - app/templates/scape.html | 2 +- app/views.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/templates/base.html b/app/templates/base.html index 965bce3..f407b49 100755 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -14,7 +14,6 @@ - {% block css %} {% endblock%} diff --git a/app/templates/scape.html b/app/templates/scape.html index 96848b2..3adea7a 100644 --- a/app/templates/scape.html +++ b/app/templates/scape.html @@ -3,7 +3,7 @@ - +