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* diff --git a/app/__init__.py b/app/__init__.py index ceebaa6..fa1b60b 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,4 +36,4 @@ socketio = SocketIO(app) app.config.from_object(__name__) from app import views -flask_whooshalchemyplus.init_app(app) # initialize + diff --git a/app/forms.py b/app/forms.py index 0db4fdf..c3aacd0 100755 --- a/app/forms.py +++ b/app/forms.py @@ -3,8 +3,10 @@ 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 +from wtforms.fields.html5 import DecimalRangeField + # - - - Forms - - - class AuthorForm(NoCsrfForm): @@ -19,12 +21,28 @@ 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) + 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()]) 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() + message = StringField('message') class ChatForm(FlaskForm): message = StringField('message', validators=[InputRequired()]) @@ -33,6 +51,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): @@ -45,6 +64,11 @@ class EditStackForm(FlaskForm): class SearchForm(FlaskForm): choices = [('All', 'All'), ('Title', 'Title'), - ('Category', 'Category')] - select = SelectField('', choices=choices) + ('Author', 'Author'), + ('Category', 'Category'), + ('Stack', 'Stack')] + select = SelectField('', choices=choices, default='All') search = StringField('', validators=[InputRequired()]) + grid = SubmitField('Grid') + listview = SubmitField('List') + randomize = SubmitField('Order differently') diff --git a/app/models.py b/app/models.py index d2361e5..11d403a 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)) @@ -27,15 +26,21 @@ 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', 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)) + 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): + 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 @@ -44,6 +49,12 @@ class Book(db.Model): self.year_published = year_published 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): @@ -85,20 +96,34 @@ class Chat(db.Model): self.message = message self.time = datetime.datetime.utcnow() - 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 + +class Potential(db.Model): + __tablename__ = 'potential' + id = db.Column(db.Integer, primary_key = True) + ptitle = db.Column(db.String(50)) + time = db.Column(DateTime, default=datetime.datetime.utcnow()) + + def __init__(self, ptitle): + self.ptitle = ptitle + self.time = datetime.datetime.utcnow() + + class AuthorSchema(Schema): id = fields.Int(dump_only=True) author_name = fields.Str() @@ -126,7 +151,6 @@ class BookSchema(Schema): stacks = fields.Nested(StackSchema, many=True) - def must_not_be_blank(data): if not data: raise ValidationError('You forgot to write stuff.') diff --git a/app/static/css/style.css b/app/static/css/style.css index f69dd38..e862ae3 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"; @@ -18,6 +7,18 @@ font-family: "Archivo Narrow"; p{ font-size: 18px; + +} + +a{ + + text-decoration: none; + color: black; +} +a:hover{ + + text-decoration: underline; + color: #0000FF; } .navigation ul{ @@ -35,7 +36,7 @@ float: left; display: block; color: black; text-align: center; - padding: 14px 16px; + padding: 14px 18px; font-size: 18px; text-decoration: none; } @@ -52,14 +53,23 @@ font-style: italic; } .container{ -padding: 0px 8px; +padding: 0px 10px; } +#fileformat{ + text-align: center; +} + .lead{ font-size: 18px; +} + +.about{ +font-size: 16px; + } .library_table{ border-spacing:0; /* Removes the cell spacing via CSS */ @@ -73,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; @@ -88,7 +101,8 @@ background-color: #E8E8E8!important; .library_table .title_col{ font-size: 20px; - padding-left: 10px; + padding-left: 90px; + padding-right: 20px; } .library_table .author_col{ @@ -121,7 +135,7 @@ cursor: pointer; .header input{ height:40px; width: 500px; -font-size: 30px; +font-size: 24px; font-weight: bold; } @@ -136,10 +150,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); } @@ -150,7 +164,7 @@ font-size: 18px; padding:6px 15px; left:0px; border:0px solid #dbdbdb; -background-color: grey; +background-color: #686d72; color:#fafafa; } @@ -184,14 +198,23 @@ font-size: 12px; } .ui-tabs-vertical { width: 100em; border-top: 0;} +<<<<<<< HEAD .ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; } .ui-tabs-vertical .ui-tabs-nav li { clear: left; width: 100%; border-bottom-width: 0 !important; border-right-width: 0 !important; margin: 0 -1px .2em 0; } +======= +.ui-tabs-vertical a { text-decoration: none; color: black;} +.ui-tabs-vertical .ui-tabs-nav { padding: .2em .2em .2em .2em; float: left; width: 15em; -webkit-appearance: none;} +.ui-tabs-vertical .ui-tabs-nav li { clear: left; width: 100%; border-bottom-width: 0 !important; border-right-width: 0 !important; margin: 0 -1px .2em 0; list-style-type: none; } +>>>>>>> stack_stuff .ui-tabs-vertical .ui-tabs-nav li a { display:block; } -.ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { padding-bottom: 0; padding-right: .1em; border-right-width: 0; background-color: #A9A9A9 !important;} -.ui-tabs-vertical .ui-tabs-panel { padding: 1em; float: left; width: 50em; font-size: 12px;} -#draggable { width: 100px; height: 100px; padding: 0.5em; float: left; margin: 10px 10px 10px 0; } -#droppable { width: 150px; height: 150px; padding: 0.5em; float: left; margin: 10px; } +.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%; @@ -200,7 +223,7 @@ font-size: 12px; top:0; left:0; position: fixed; - font-size: 20px; + font-size: 18px; background-color: yellow; } @@ -244,7 +267,7 @@ div.marquee > div.marquee-text { padding: 10px; margin: 0px; height: 100%; - background-color: #551A8B; + background-color: #b4b9be; overflow-y: scroll; overflow-x: hidden; color: white; @@ -281,7 +304,7 @@ z-index: 100000; margin:0px!important; padding:0px!important; height: 40px; - font-size: 20px; + font-size: 16px; word-wrap: break-word; word-break: break-all; float: left; @@ -306,7 +329,7 @@ box-sizing: border-box; margin-top: 20px; } .messages .msg{ - font-size: 30px; + font-size: 24px; margin: 0px; margin-top: -15px; margin-bottom: 10px; @@ -320,9 +343,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; } @@ -332,3 +357,77 @@ 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: top; + 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; +} + +/* 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.5); /* Black w/ opacity */ +} + +/* Modal Content/Box */ +.modal-content { + background-color: yellow; + margin: 15% auto; /* 15% from the top and centered */ + padding: 15px; + border: 1px solid #888; + width: 40%; /* Could be more or less, depending on screen size */ +} + +/* The Close Button */ +.close { + color: grey; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: red; + text-decoration: none; + cursor: pointer; +} diff --git a/app/static/img/default_cover.gif b/app/static/img/default_cover.gif new file mode 100644 index 0000000..500390c Binary files /dev/null and b/app/static/img/default_cover.gif differ diff --git a/app/static/js/app.js b/app/static/js/app.js index 29adeff..10eb513 100755 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -191,3 +191,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/about.html b/app/templates/about.html index 802a4dc..67aae18 100755 --- a/app/templates/about.html +++ b/app/templates/about.html @@ -2,12 +2,29 @@ {% block main %}

About

-

+

XPPL is a project aimed at people who are studying the field of media culture, or as we like to call them: knowledge comrades.
-This digital library gathers all the books and articles floating around on PZI shelves and our hard drives and memory sticks, so that they can be shared. +
+This digital library gathers all the books and articles floating around on the shelves of the Piet Zwart Institute, and our hard drives and memory sticks, so that they can be shared, annotated and grouped together into stacks... +

Its web interface hosts a curated catalogue of books and articles, and its distributed architecture provides instances for uploading and downloading. +

It starts at XPUB, but can go anywhere we want it to.

+ + +

What's the deal with the stacks?

+

+ A stack is a number of books that are read at a certain point in time, alternating between them. They usually have a topic in common, or follow a certain study path that can bring you to a point of knowledge. Rather than a bookshelf, where books are lined up and often forgotten, the stack on your table/nightstand/toilet consists of books prone to be opened and reopened at any time. +

+ +

Who's behind this?

+

+We're Angeliki, Alex, Alice, Joca, Natasha and Zalán, helped and supported by Femke, Aymeric, Michael, Steve, Andre, Leslie and many more. +
+
+XPUB is a study path within the Piet Zwart Institute masters program. +

{% endblock %} diff --git a/app/templates/add_book.html b/app/templates/add_book.html index c01dc37..6326174 100755 --- a/app/templates/add_book.html +++ b/app/templates/add_book.html @@ -1,7 +1,24 @@ {% extends 'base.html' %} {% block main %} -
+{% from "_formhelpers.html" import render_field %} + + + + +
+
+

Add Book

{% with messages = get_flashed_messages() %} {% if messages %} @@ -16,11 +33,10 @@ {% endwith %}
{{ form.csrf_token }} -
{{ form.title.label }} {{ form.title(size=20, class="form-control") }}
+
Title:*
{{ form.title (size=50, class="form-control") }}

- {{ form.author.label }} + Author(s):* @@ -28,21 +44,91 @@ {% for author in form.author %} - + {% endfor %}
{{ author.author_name }}{{ author.author_name (size=50)}}

-
{{ form.category.label }} {{ form.category(size=20, 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
-
{{ form.year_published.label }} {{ form.year_published(size=4, 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/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/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 c936ff4..2fa5268 100755 --- a/app/templates/edit_book_detail.html +++ b/app/templates/edit_book_detail.html @@ -7,12 +7,12 @@ - + {{ form.csrf_token }}

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

- +

@@ -31,11 +31,23 @@ {% endfor %}

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

-
- {{ form.year_published.label }} {{ form.year_published(size=4, class="form-control") }} +
+ Category: {{ form.category(size=20, + 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/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/potential_pdf.html b/app/templates/potential_pdf.html new file mode 100644 index 0000000..866acec --- /dev/null +++ b/app/templates/potential_pdf.html @@ -0,0 +1,43 @@ + + + + + + + + + + + XPPL + + +
    +

    A Potential PDF

    + +

    Every book ever requested in the XPPL library:

    +
    +

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

    {% endfor %} +
    +
    +
    +   ,   ,
    +  /////|
    + ///// |
    +|~~~|  |
    +|===|  |
    +|x  |  |
    +| p |  |
    +|p l| /
    +|===|/
    +'---'
    +
    + + +
    +
    + diff --git a/app/templates/red_link.html b/app/templates/red_link.html index 43a331a..22f101d 100755 --- a/app/templates/red_link.html +++ b/app/templates/red_link.html @@ -1,18 +1,14 @@ {% extends "base.html" %} -{% block title %}Red link page{% endblock %} {% block css %} {% endblock %} -{% block header %}{% endblock %} - {% block main %} -

    We don't have any results for: {{ title }}

    -

    upload?

    -

    go back home?

    - - +

    Nothing in the library yet with: {{ title }}

    +
    -

    Add Book

    {% with messages = get_flashed_messages() %} {% if messages %}
    @@ -42,12 +32,19 @@
    {% endif %} {% endwith %} +
    + +
    +
    +
    +

    Add this book:

    + {{ form.csrf_token }} -
    {{ form.title.label }} {{ form.title(size=20, class="form-control") }}
    +
    Title: {{ form.title (size=34, class="form-control") }}

    - {{ form.author.label }} @@ -56,17 +53,83 @@ {% for author in form.author %} - + {% endfor %}
    {{ author.author_name }}{{ author.author_name (size=40)}}
    +
    +
    + Category: {{ form.category(size=27, class="form-control") }} +
    +
    + Year published: {{ form.year_published(size=8, class="form-control") }} +

    -
    {{ form.category.label }} {{ form.category(size=20, 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 aa1acf7..296dc8e 100644 --- a/app/templates/results.html +++ b/app/templates/results.html @@ -1,18 +1,24 @@ {% extends 'base.html' %} {% block main %} -
    {% from "_formhelpers.html" import render_field %}
    {{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
    - -
    - -
    -

    Search Results for: {{ query }}

    + +


    + {{ 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 %}
    @@ -25,20 +31,21 @@ {% endif %} {% endwith %} - +
    - + - - - - - + + + + + {% for book in books %} - + - + - - - {% endfor %} + {% endfor %} +
    + {{ book.title }} {% for author in book.authors %} @@ -46,40 +53,40 @@
  • {{ author.author_name }}
  • {% endfor %}
    {{ book.fileformat }}{{ book.year_published or '––'}} {{ book.category}} {% for stack in book.stacks %}
  • {{ stack.stack_name }}
  • {% endfor %}
    + ==>
    +
    -
    -
    -

    Other books

    +
    +

    More books

    - + - - - - - + + + + + {% for book in books_all %} - + - + - + {% endfor %}
    {{ book.title }} {% for author in book.authors %} @@ -87,16 +94,18 @@
  • {{ author.author_name }}
  • {% endfor %}
    {{ book.fileformat }}{{ book.year_published or '––'}} {{ book.category}} {% for stack in book.stacks %}
  • {{ stack.stack_name }}
  • - {% endfor %} + {% endfor %} +
    + ==>

    diff --git a/app/templates/results_grid.html b/app/templates/results_grid.html new file mode 100644 index 0000000..228f7db --- /dev/null +++ b/app/templates/results_grid.html @@ -0,0 +1,87 @@ +{% 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 }} ({{ book.year_published or '?' }}) +
    + +{% for author in book.authors %} +{{ author.author_name }}
    {% endfor %}
    + +

    +
    + {% endfor %} +
    + +
    +

    More books

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

    + + +{{ book.title }} ({{ book.year_published or '?' }}) +
    + +{% 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 77cbddb..d2e3f0d 100755 --- a/app/templates/show_book_detail.html +++ b/app/templates/show_book_detail.html @@ -2,23 +2,71 @@ {% block main %}
    -

    {{ book.title }}

    - -

    Author(s):

    - -

    Category: {{ book.category }}

    -

    Year published: {{ book.year_published }}

    +
    +
    -

    Stack(s):

      {% for stack in book.stacks %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - {% 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 '?' }}
      +<<<<<<< HEAD download {{ book.fileformat }}
      @@ -31,6 +79,31 @@

      edit +======= +{% if book.file %} + + +{% else %} +{% endif %} +>>>>>>> stack_stuff + + +

      + +
      +{% 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/templates/show_books.html b/app/templates/show_books.html index e4860e7..04a9d0b 100755 --- a/app/templates/show_books.html +++ b/app/templates/show_books.html @@ -8,8 +8,11 @@
      {{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}
      - - + + +


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

      +

      All Books

      {% with messages = get_flashed_messages() %} @@ -27,21 +30,21 @@ - + - - - - - + + + + + - {% for book in books|sort(attribute='title', reverse = False) %} + {% for book in books %} - + +
      + +{% for author in book.authors %} +{{ author.author_name }}
      {% endfor %}
      + +

      + {% endfor %} - + {% endblock %} diff --git a/app/templates/show_stack_detail.html b/app/templates/show_stack_detail.html index af144cd..f55d6a2 100644 --- a/app/templates/show_stack_detail.html +++ b/app/templates/show_stack_detail.html @@ -6,10 +6,18 @@

      {{ stack.stack_name }}

      {{ stack.stack_description }}

      -

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

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

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

      Books in this stack:

      +

      + {% for book in stack.books %}

    • {{book.title}}
    • - +
      @@ -19,12 +27,13 @@

      - Delete

      - Edit

      + Remove stack

      +

      + Edit title and/or description

      -

      Go back to stacks

      +

      Go back to the other stacks

      {% endblock %} diff --git a/app/templates/show_stack_detail_tab.html b/app/templates/show_stack_detail_tab.html index d074c2c..cf32045 100644 --- a/app/templates/show_stack_detail_tab.html +++ b/app/templates/show_stack_detail_tab.html @@ -10,8 +10,15 @@

      {{ stack.stack_description }}

      +

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

      +{% else %} -

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

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

    • {{book.title}}
    • diff --git a/app/templates/show_stacks.html b/app/templates/show_stacks.html index f1388a5..51f1a17 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

      -
      + - + + {% for book in books %} +
      + + +

      +

      +{{ book.title }} ({{ book.year_published or '–' }})
      @@ -25,6 +23,13 @@ + +
      +
      +
      +
      +
      +


      diff --git a/app/views.py b/app/views.py index 4f84bf8..b84e2b6 100755 --- a/app/views.py +++ b/app/views.py @@ -11,13 +11,15 @@ from flask_weasyprint import HTML, render_pdf import json import os 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 +from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema, Potential from app.cover import get_cover from app.getannot import get_annotations from urllib.parse import quote as urlquote from os import environ from flask_socketio import SocketIO, emit +from weasyprint import HTML import datetime import time import autocomplete @@ -47,6 +49,7 @@ def allowed_file(filename): def home(): chat_form = ChatForm() chat_messages = db.session.query(Chat).all() + username = 'librarian' # if request.method == 'POST': # if chat_form.validate_on_submit(): @@ -55,7 +58,7 @@ def home(): # db.session.add(msg) # db.session.commit() - return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username="librarian") + return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username) @app.route('/hello/') def hello(name): @@ -109,6 +112,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 @@ -120,7 +132,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']) @@ -136,24 +148,23 @@ def remove_book_by_id(id): @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(): - # 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 + message = user_form.message.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 book.year_published = year_published + book.message = message #authors update book.authors.clear() @@ -165,6 +176,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): + allbooks = db.session.query(Book).all() + id = book.id + + filename = str(id) + "_" + secure_filename(file.filename) + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + name, file_extension = os.path.splitext(filename) + file.save(fullpath) + book.cover = get_cover(fullpath, name) + book.file = 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)) @@ -175,6 +206,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(): @@ -182,7 +224,14 @@ 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 + 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 @@ -199,27 +248,34 @@ def add_book(): 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) + filename = str(id) + "_" + secure_filename(file.filename) + + fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename) + name, file_extension = os.path.splitext(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) + #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, message, sameness, diversity, gender, who, time) db.session.add(book) for author in authors: author_name = author.get("author_name") @@ -235,7 +291,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 @@ -277,9 +333,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')) @@ -324,49 +381,140 @@ 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 + +@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 +view = ['1'] @app.route('/books', methods= ['POST','GET']) def show_books(): +<<<<<<< HEAD autocomplete.load() #Train markov model once, for autocomplete in search books = db.session.query(Book).all() +======= + books = db.session.query(Book).order_by(Book.title) +>>>>>>> stack_stuff search = SearchForm(request.form) + view.append('1') + viewby = '1' + + if search.grid.data: + viewby = '2' + view.append('2') + return render_template ('show_books_grid.html', books=books, form=search) + + if search.listview.data: + viewby = '1' + view.append('1') + 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))) + newmsg = 'searched for: ' + search.search.data + socketio.emit('channel-' + str(1), { + 'username': 'Search form', + 'text': search.search.data, + 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M"))}, broadcast=True) + # Save message + my_new_chat = Chat(message=newmsg) + db.session.add(my_new_chat) + try: + db.session.commit() + except: + db.session.rollback() + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) 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)).all() +@app.route('/search///', methods=['POST', 'GET']) +def search_results(searchtype, query, viewby): + search = SearchForm(request.form, search=query) + results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) + allbooks = set(Book.query.all()) + viewby = view[-1] if searchtype == 'Title': - results=Book.query.filter(Book.title.contains(query)).all() + results=Book.query.filter(Book.title.contains(query)).order_by(Book.title) if searchtype == 'Category': - results=Book.query.filter(Book.category.contains(query)).all() + results=Book.query.filter(Book.category.contains(query)).order_by(Book.title) - if searchtype== 'All': - results=Book.query.whoosh_search(query).all() + if searchtype== 'Author': + results=db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)).order_by(Book.title) + + if searchtype== 'Stack': + results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)).order_by(Book.title) +<<<<<<< HEAD # if searchtype== 'Annotation': # results=Book.query.filter(Book.category.contains(query)).all() if not results: +======= + 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(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))) + 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')) +>>>>>>> stack_stuff 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() + 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=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=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage) if request.method == 'POST': + newmsg = 'searched for: ' + search.search.data + socketio.emit('channel-' + str(1), { + 'username': 'Search form', + 'text': search.search.data, + 'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M"))}, broadcast=True) + # Save message + my_new_chat = Chat(message=newmsg) + db.session.add(my_new_chat) + try: + db.session.commit() + except: + db.session.rollback() + query = search.search.data results = [] - return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data))) + if viewby == '1': + print (view[-1]) + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) + else: + return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby))) - return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query) + if viewby == '2': + 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) +<<<<<<< HEAD ## Search - autocomplete autocomplete_suggestions = [] autocomplete.load() #Train markov model once, for autocomplete in search @@ -392,6 +540,33 @@ def test1(): return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json') ## STACKS! +======= + else: + 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 +# 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') +>>>>>>> stack_stuff @app.route('/add_to_stack/', methods=['GET', 'POST']) def add_to_stack(id): @@ -520,7 +695,6 @@ def new_message(message): db.session.rollback() - if __name__ == '__main__': # socketio.run(app) app.run(debug=True,host="0.0.0.0",port="8080") diff --git a/import_csv.py b/import_csv.py index b43447d..df681c0 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(',') @@ -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) diff --git a/run.py b/run.py index 3630a9b..530a462 100755 --- a/run.py +++ b/run.py @@ -1,4 +1,9 @@ #! /usr/bin/env python from app import app, socketio +<<<<<<< HEAD # socketio.run(app) app.run(debug=True,host="0.0.0.0",port=8080) +======= +socketio.run(app, port=8080) +#app.run(debug=True,host="0.0.0.0",port=8080) +>>>>>>> stack_stuff diff --git a/whoosh/Book/MAIN_taxstcerlfadcokk.seg b/whoosh/Book/MAIN_taxstcerlfadcokk.seg new file mode 100644 index 0000000..a10c222 Binary files /dev/null and b/whoosh/Book/MAIN_taxstcerlfadcokk.seg differ